---
title: "XML Project"
description: "Derafu XML"
type: "docs"
category: "doc"
tags: [php]
authors: [Anonymous]
date: "2026-08-24"
last_update: "2026-08-24"
time_minutes: 1
draft: false
unlisted: false
url: "https://www.derafu.dev/docs/data/xml"
---

# Derafu XML



---

## Introduction

Library for XML manipulation

# Library for XML manipulation

![GitHub last commit](https://img.shields.io/github/last-commit/derafu/xml/main)
![CI Workflow](https://github.com/derafu/xml/actions/workflows/ci.yml/badge.svg?branch=main&amp;event=push)
![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/derafu/xml)
![GitHub Issues](https://img.shields.io/github/issues-raw/derafu/xml)
![Total Downloads](https://poser.pugx.org/derafu/xml/downloads)
![Monthly Downloads](https://poser.pugx.org/derafu/xml/d/monthly)

A comprehensive PHP library for XML manipulation, providing robust tools for encoding, decoding, validating, and querying XML documents.

## Features

- **Conversion**: Transform between XML and PHP arrays in both directions.
- **Validation**: Validate XML documents against XSD schemas.
- **Querying**: Powerful XPath querying with parameter support.
- **Canonicalization**: Support for C14N and ISO-8859-1 encoding.
- **Encoding**: Proper handling of character encoding between UTF-8 and ISO-8859-1.
- **Special Characters**: Automatic handling of XML special characters and entities.

## Installation

```bash
composer require derafu/xml
```

## Basic Usage

### Creating XML from an Array

```php
use Derafu\Xml\Service\XmlEncoder;

$encoder = new XmlEncoder();

// Create an array to convert to XML.
$data = [
    &#039;root&#039; =&gt; [
        &#039;element1&#039; =&gt; &#039;value1&#039;,
        &#039;element2&#039; =&gt; &#039;value2&#039;,
        &#039;element3&#039; =&gt; [
            &#039;@attributes&#039; =&gt; [
                &#039;attr1&#039; =&gt; &#039;attrValue&#039;,
            ],
            &#039;@value&#039; =&gt; &#039;value3&#039;,
        ],
        &#039;repeatedElement&#039; =&gt; [&#039;value4&#039;, &#039;value5&#039;, &#039;value6&#039;],
    ],
];

// Convert the array to XML.
$xmlDocument = $encoder-&gt;encode($data);

// Save as XML string.
$xmlString = $xmlDocument-&gt;saveXml();

// Get the XML without the XML declaration.
$xmlContent = $xmlDocument-&gt;getXml();

// Get a canonicalized version of the XML.
$c14nXml = $xmlDocument-&gt;C14N();

// Get a canonicalized version with ISO-8859-1 encoding.
$isoXml = $xmlDocument-&gt;C14NWithIso88591Encoding();
```

### Converting XML to an Array

```php
use Derafu\Xml\Service\XmlDecoder;
use Derafu\Xml\XmlDocument;

$decoder = new XmlDecoder();

// Load an existing XML string.
$xmlContent = &#039;&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&lt;root&gt;&lt;element&gt;value&lt;/element&gt;&lt;/root&gt;&#039;;
$document = new XmlDocument();
$document-&gt;loadXml($xmlContent);

// Convert to an array.
$array = $decoder-&gt;decode($document);

// Now $array contains [&#039;root&#039; =&gt; [&#039;element&#039; =&gt; &#039;value&#039;]]
```

### Working with XPath

```php
use Derafu\Xml\XPathQuery;

$xmlContent = &#039;&lt;?xml version=&quot;1.0&quot;?&gt;&lt;root&gt;&lt;item id=&quot;1&quot;&gt;First&lt;/item&gt;&lt;item id=&quot;2&quot;&gt;Second&lt;/item&gt;&lt;/root&gt;&#039;;

// Create XPath query instance.
$query = new XPathQuery($xmlContent);

// Get a specific value.
$value = $query-&gt;getValue(&#039;/root/item[@id=&quot;2&quot;]&#039;); // &quot;Second&quot;

// Get multiple values.
$values = $query-&gt;getValues(&#039;/root/item&#039;); // [&quot;First&quot;, &quot;Second&quot;]

// Get structured array.
$array = $query-&gt;get(&#039;/root&#039;);
// Result: [&#039;item&#039; =&gt; [&#039;First&#039;, &#039;Second&#039;]]
```

### XML Validation

```php
use Derafu\Xml\Exception\XmlException;
use Derafu\Xml\Service\XmlValidator;

$validator = new XmlValidator();

// Validate an XML document against a schema.
try {
    $validator-&gt;validate($xmlDocument, &#039;/path/to/schema.xsd&#039;);
    echo &quot;XML is valid!&quot;;
} catch (XmlException $e) {
    echo &quot;Validation failed: &quot; . $e-&gt;getMessage();
    $errors = $e-&gt;getErrors(); // Get detailed error information.
}
```

## Advanced Usage

### Working with Namespaces

```php
// Create XPath query with namespace support.
$namespaces = [&#039;ns&#039; =&gt; &#039;http://example.com/namespace&#039;];
$query = new XPathQuery($xmlContent, $namespaces);

// Query with namespace.
$result = $query-&gt;get(&#039;//ns:Element&#039;);
```

### Array Structure for XML Creation

When creating XML from arrays, the structure follows these conventions:

- Simple key-value pairs become element nodes.
- Use `@attributes` for node attributes.
- Use `@value` for node text content when attributes are present.
- Arrays of values create repeated nodes with the same name.

Example:
```php
$data = [
    &#039;root&#039; =&gt; [
        &#039;element&#039; =&gt; [
            &#039;@attributes&#039; =&gt; [&#039;id&#039; =&gt; &#039;123&#039;],
            &#039;@value&#039; =&gt; &#039;content&#039;,
        ],
        &#039;items&#039; =&gt; [
            &#039;item&#039; =&gt; [&#039;value1&#039;, &#039;value2&#039;, &#039;value3&#039;],
        ],
    ],
];
```

Produces:
```xml
&lt;root&gt;
  &lt;element id=&quot;123&quot;&gt;content&lt;/element&gt;
  &lt;items&gt;
    &lt;item&gt;value1&lt;/item&gt;
    &lt;item&gt;value2&lt;/item&gt;
    &lt;item&gt;value3&lt;/item&gt;
  &lt;/items&gt;
&lt;/root&gt;
```




---

## Array Structure

XML-Array Conversion Structure

# XML-Array Conversion Structure

The Derafu XML library uses a specific convention for representing the structure of XML documents as PHP arrays and vice versa. Understanding this structure is essential for effectively using the encoding and decoding features.

## Basic Structure

At its most basic level, an XML element with a value is represented as a key-value pair in the array:

XML:
```xml
&lt;element&gt;value&lt;/element&gt;
```

Array:
```php
[&#039;element&#039; =&gt; &#039;value&#039;]
```

## Nested Elements

Nested elements are represented as nested arrays:

XML:
```xml
&lt;parent&gt;
  &lt;child&gt;value&lt;/child&gt;
&lt;/parent&gt;
```

Array:
```php
[
    &#039;parent&#039; =&gt; [
        &#039;child&#039; =&gt; &#039;value&#039;
    ]
]
```

## Attributes

XML attributes are handled using the special `@attributes` key:

XML:
```xml
&lt;element id=&quot;123&quot; type=&quot;example&quot;&gt;value&lt;/element&gt;
```

Array:
```php
[
    &#039;element&#039; =&gt; [
        &#039;@attributes&#039; =&gt; [
            &#039;id&#039; =&gt; &#039;123&#039;,
            &#039;type&#039; =&gt; &#039;example&#039;
        ],
        &#039;@value&#039; =&gt; &#039;value&#039;
    ]
]
```

Note the use of `@value` to hold the element&#039;s text content when attributes are present.

## Repeated Elements

Elements with the same name are collected into arrays:

XML:
```xml
&lt;parent&gt;
  &lt;child&gt;value1&lt;/child&gt;
  &lt;child&gt;value2&lt;/child&gt;
  &lt;child&gt;value3&lt;/child&gt;
&lt;/parent&gt;
```

Array:
```php
[
    &#039;parent&#039; =&gt; [
        &#039;child&#039; =&gt; [
            &#039;value1&#039;,
            &#039;value2&#039;,
            &#039;value3&#039;
        ]
    ]
]
```

## Complex Structures

The conventions can be combined to represent complex XML structures:

XML:
```xml
&lt;root&gt;
  &lt;items&gt;
    &lt;item id=&quot;1&quot;&gt;
      &lt;name&gt;Item 1&lt;/name&gt;
      &lt;price currency=&quot;USD&quot;&gt;19.99&lt;/price&gt;
    &lt;/item&gt;
    &lt;item id=&quot;2&quot;&gt;
      &lt;name&gt;Item 2&lt;/name&gt;
      &lt;price currency=&quot;EUR&quot;&gt;29.99&lt;/price&gt;
    &lt;/item&gt;
  &lt;/items&gt;
  &lt;summary total=&quot;2&quot;&gt;Multiple items&lt;/summary&gt;
&lt;/root&gt;
```

Array:
```php
[
    &#039;root&#039; =&gt; [
        &#039;items&#039; =&gt; [
            &#039;item&#039; =&gt; [
                [
                    &#039;@attributes&#039; =&gt; [&#039;id&#039; =&gt; &#039;1&#039;],
                    &#039;name&#039; =&gt; &#039;Item 1&#039;,
                    &#039;price&#039; =&gt; [
                        &#039;@attributes&#039; =&gt; [&#039;currency&#039; =&gt; &#039;USD&#039;],
                        &#039;@value&#039; =&gt; &#039;19.99&#039;
                    ]
                ],
                [
                    &#039;@attributes&#039; =&gt; [&#039;id&#039; =&gt; &#039;2&#039;],
                    &#039;name&#039; =&gt; &#039;Item 2&#039;,
                    &#039;price&#039; =&gt; [
                        &#039;@attributes&#039; =&gt; [&#039;currency&#039; =&gt; &#039;EUR&#039;],
                        &#039;@value&#039; =&gt; &#039;29.99&#039;
                    ]
                ]
            ]
        ],
        &#039;summary&#039; =&gt; [
            &#039;@attributes&#039; =&gt; [&#039;total&#039; =&gt; &#039;2&#039;],
            &#039;@value&#039; =&gt; &#039;Multiple items&#039;
        ]
    ]
]
```

## Special Cases

### Empty Elements

Empty elements are represented as `null` or empty strings:

XML:
```xml
&lt;element&gt;&lt;/element&gt;
```

Array:
```php
[&#039;element&#039; =&gt; null]
```

or when creating XML:

```php
[&#039;element&#039; =&gt; &#039;&#039;]
```

### Skipping Elements

When creating XML, certain values cause an element to be skipped:

```php
[
    &#039;included&#039; =&gt; &#039;This will be in the XML&#039;,
    &#039;excluded&#039; =&gt; null,       // This will be skipped.
    &#039;alsoExcluded&#039; =&gt; false,  // This will be skipped.
    &#039;emptyArraySkipped&#039; =&gt; [] // This will be skipped.
]
```

## Array to XML Encoding Rules

When using `XmlEncoder` to create XML from arrays, these rules apply:

1. Simple key-value pairs become elements with text content.
2. Nested arrays become nested elements.
3. The special key `@attributes` defines attributes for the parent element.
4. The special key `@value` defines the text content when attributes are present.
5. Arrays of values create multiple elements with the same name.
6. Null, false, and empty arrays are skipped (not included in the XML).
7. Empty strings (&#039;&#039;) create empty elements.

## XML to Array Decoding Rules

When using `XmlDecoder` to convert XML to arrays, these rules apply:

1. Elements become keys in the array.
2. Text content becomes the value of the key.
3. Attributes are collected under the `@attributes` key.
4. When attributes are present, text content is stored under the `@value` key.
5. Multiple elements with the same name are collected into arrays.
6. Empty elements become null values.
7. Complex nested structures are preserved in the array hierarchy.

## Usage Examples

### Creating Complex XML

```php
use Derafu\Xml\Service\XmlEncoder;

$data = [
    &#039;invoice&#039; =&gt; [
        &#039;@attributes&#039; =&gt; [
            &#039;id&#039; =&gt; &#039;INV-2025-001&#039;,
            &#039;date&#039; =&gt; &#039;2025-03-05&#039;
        ],
        &#039;customer&#039; =&gt; [
            &#039;name&#039; =&gt; &#039;Acme Inc.&#039;,
            &#039;address&#039; =&gt; [
                &#039;street&#039; =&gt; &#039;123 Main St&#039;,
                &#039;city&#039; =&gt; &#039;Anytown&#039;,
                &#039;zipcode&#039; =&gt; &#039;12345&#039;
            ]
        ],
        &#039;items&#039; =&gt; [
            &#039;item&#039; =&gt; [
                [
                    &#039;@attributes&#039; =&gt; [&#039;sku&#039; =&gt; &#039;PROD1&#039;],
                    &#039;description&#039; =&gt; &#039;Product One&#039;,
                    &#039;quantity&#039; =&gt; &#039;2&#039;,
                    &#039;price&#039; =&gt; &#039;10.99&#039;
                ],
                [
                    &#039;@attributes&#039; =&gt; [&#039;sku&#039; =&gt; &#039;PROD2&#039;],
                    &#039;description&#039; =&gt; &#039;Product Two&#039;,
                    &#039;quantity&#039; =&gt; &#039;1&#039;,
                    &#039;price&#039; =&gt; &#039;24.99&#039;
                ]
            ]
        ],
        &#039;total&#039; =&gt; &#039;46.97&#039;
    ]
];

$encoder = new XmlEncoder();

$xmlDocument = $encoder-&gt;encode($data);
echo $xmlDocument-&gt;saveXml();
```

### Processing XML into Arrays

```php
use Derafu\Xml\Service\XmlDecoder;

$xmlString = &#039;
&lt;catalog&gt;
    &lt;book id=&quot;bk101&quot;&gt;
        &lt;author&gt;Gambardella, Matthew&lt;/author&gt;
        &lt;title&gt;XML Developer\&#039;s Guide&lt;/title&gt;
        &lt;genre&gt;Computer&lt;/genre&gt;
        &lt;price&gt;44.95&lt;/price&gt;
        &lt;publish_date&gt;2025-01-15&lt;/publish_date&gt;
    &lt;/book&gt;
    &lt;book id=&quot;bk102&quot;&gt;
        &lt;author&gt;Ralls, Kim&lt;/author&gt;
        &lt;title&gt;Midnight Rain&lt;/title&gt;
        &lt;genre&gt;Fantasy&lt;/genre&gt;
        &lt;price&gt;5.95&lt;/price&gt;
        &lt;publish_date&gt;2025-02-20&lt;/publish_date&gt;
    &lt;/book&gt;
&lt;/catalog&gt;&#039;;

$document = new \Derafu\Xml\XmlDocument();
$document-&gt;loadXml($xmlString);

$decoder = new XmlDecoder();

$array = $decoder-&gt;decode($document);

// Access data directly.
$firstBookTitle = $array[&#039;catalog&#039;][&#039;book&#039;][0][&#039;title&#039;]; // &quot;XML Developer&#039;s Guide&quot;
$secondBookPrice = $array[&#039;catalog&#039;][&#039;book&#039;][1][&#039;price&#039;]; // &quot;5.95&quot;
```

## Tips for Working with the Array Structure

1. **Plan Your Structure**: When creating XML, plan your array structure carefully to match the desired XML output.
2. **Handle Repeated Elements**: Always use numeric arrays for repeated elements with the same name.
3. **Use `@attributes` and `@value`**: Remember to use these special keys when working with elements that have both attributes and text content.
4. **Validate Your Output**: Always validate the generated XML against your schema if one exists.
5. **Check for Empty Values**: When processing arrays from XML, check for null values to handle empty elements properly.




---

## XML Document

XmlDocument Class

# XmlDocument Class

The `XmlDocument` class extends PHP&#039;s native `DOMDocument` with additional functionality for XML manipulation, transformation, and querying. This class is the core component for working with XML documents in the Derafu XML library.

## Key Features

- Extended XML loading with character encoding handling.
- Simplified access to XML metadata (namespace, schema).
- Canonicalization support with encoding options.
- XPath query integration.
- Array conversion capabilities.
- Signature node handling for XML digital signatures.

## Basic Usage

### Creating a New Document

```php
use Derafu\Xml\XmlDocument;

// Create with default version (1.0) and encoding (ISO-8859-1).
$document = new XmlDocument();

// Or specify version and encoding.
$document = new XmlDocument(&#039;1.0&#039;, &#039;UTF-8&#039;);
```

### Loading XML Content

```php
// Load from a string.
$xmlString = &#039;&lt;root&gt;&lt;element&gt;value&lt;/element&gt;&lt;/root&gt;&#039;;
$document-&gt;loadXml($xmlString);

// The method handles encoding conversion automatically.
$utf8Xml = &#039;&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&lt;root&gt;&lt;element&gt;Árbol&lt;/element&gt;&lt;/root&gt;&#039;;
$document-&gt;loadXml($utf8Xml); // Will convert to ISO-8859-1 if needed.
```

### Accessing Document Information

```php
// Get the root element name.
$rootName = $document-&gt;getName(); // e.g., &quot;root&quot;

// Get the XML namespace (if any).
$namespace = $document-&gt;getNamespace(); // e.g., &quot;http://example.com&quot;

// Get the schema location (if any).
$schema = $document-&gt;getSchema(); // e.g., &quot;schema.xsd&quot;
```

### Saving and Serializing

```php
// Get the complete XML document with declaration.
$xmlString = $document-&gt;saveXml();

// Get only the XML content without the declaration.
$xmlContent = $document-&gt;getXml();

// Get the canonicalized (C14N) version.
$canonXml = $document-&gt;C14N();

// Get canonicalized version with ISO-8859-1 encoding.
$isoCanonXml = $document-&gt;C14NWithIso88591Encoding();

// Get flattened canonicalized version (whitespace removed between tags).
$flatXml = $document-&gt;C14NWithIso88591EncodingFlattened();
```

## XPath Querying

The XmlDocument class has integrated XPath querying capabilities:

```php
// Execute an XPath query and get result as string or array.
$result = $document-&gt;query(&#039;/root/element&#039;);

// Get DOMNodeList from an XPath query.
$nodes = $document-&gt;getNodes(&#039;/root/element&#039;);

// Use parameters in XPath queries.
$params = [&#039;id&#039; =&gt; &#039;123&#039;];
$result = $document-&gt;query(&#039;/root/element[@id=:id]&#039;, $params);
```

## Array Conversion

```php
// Convert the entire document to an array.
$array = $document-&gt;toArray();

// Access a specific part using dot notation.
$value = $document-&gt;get(&#039;root.element&#039;);

// With a default value if not found.
$value = $document-&gt;get(&#039;root.missing&#039;, &#039;default value&#039;);
```

## Working with XML Digital Signatures

If the document contains an XML digital signature, you can extract it:

```php
// Get the signature node as XML.
$signatureXml = $document-&gt;getSignatureNodeXml();

// Returns null if no signature is present.
if ($signatureXml !== null) {
    // Process signature...
}
```

## Handling Special Characters and Entities

The `XmlDocument` class works with the `XmlHelper` to properly handle special characters and entities:

```php
// When saving XML, entities are handled correctly.
$document-&gt;loadXml(&#039;&lt;root&gt;&lt;element&gt;Text with &amp; &lt; &gt; &quot; \&#039;&lt;/element&gt;&lt;/root&gt;&#039;);
$xml = $document-&gt;saveXml();
// Produces: &lt;root&gt;&lt;element&gt;Text with &amp;amp; &amp;lt; &amp;gt; &amp;quot; &amp;apos;&lt;/element&gt;&lt;/root&gt;
```

## Advanced Canonicalization

### Working with Specific Nodes

You can apply canonicalization to specific parts of the document:

```php
// Canonicalize only a portion of the document.
$canonXml = $document-&gt;C14NWithIso88591Encoding(&#039;/root/section&#039;);
```

### Differences Between Canonicalization Methods

The class offers several canonicalization methods:

1. **C14N()**: Standard canonicalization, always outputs UTF-8.
2. **C14NWithIso88591Encoding()**: Canonicalization with conversion to ISO-8859-1.
3. **C14NWithIso88591EncodingFlattened()**: ISO-8859-1 canonicalization with whitespace removal.

These methods are particularly useful when working with digital signatures or when XML needs to be processed by systems with specific encoding requirements.

## Error Handling

The `loadXml()` method throws descriptive exceptions when it encounters errors:

```php
use Derafu\Xml\Exception\XmlException;

try {
    $document-&gt;loadXml($potentiallyInvalidXml);
} catch (XmlException $e) {
    echo &quot;Error loading XML: &quot; . $e-&gt;getMessage();
    $errors = $e-&gt;getErrors(); // Get detailed error information if available.
}
```

## Performance Considerations

- When working with large XML documents, prefer using XPath queries to extract specific sections rather than converting the entire document to an array.
- The canonicalization methods perform more processing, so use them only when needed.
- The `getXml()` method is more efficient than `saveXml()` when you only need the XML content without the declaration.




---

## XPath Query

XPathQuery Class

# XPathQuery Class

The `XPathQuery` class is a powerful tool for extracting data from XML documents using XPath expressions. It provides a convenient interface to query XML with support for namespaces, parameterized queries, and complex data structures.

## Basic Usage

```php
use Derafu\Xml\XPathQuery;

// Initialize with XML string or DOMDocument.
$query = new XPathQuery($xmlString);

// Get a single value.
$value = $query-&gt;getValue(&#039;/root/element&#039;);

// Get multiple values.
$values = $query-&gt;getValues(&#039;/root/items/item&#039;);

// Get DOM nodes.
$nodes = $query-&gt;getNodes(&#039;/root/items/item&#039;);

// Get structured data.
$data = $query-&gt;get(&#039;/root&#039;);
```

## Working with Namespaces

```php
// Initialize with namespace support.
$namespaces = [
    &#039;ns&#039; =&gt; &#039;http://example.com/ns&#039;,
    &#039;xs&#039; =&gt; &#039;http://www.w3.org/2001/XMLSchema&#039;
];

$query = new XPathQuery($xmlString, $namespaces);

// Use namespaces in queries.
$result = $query-&gt;get(&#039;//ns:Element/xs:Type&#039;);
```

## Parameterized Queries

The `XPathQuery` class supports parameterized queries, making it safer and easier to build dynamic XPath expressions:

```php
// Query with parameters.
$params = [
    &#039;id&#039; =&gt; &#039;123&#039;,
    &#039;type&#039; =&gt; &#039;product&#039;
];

$result = $query-&gt;get(&#039;//item[@id=:id and @type=:type]&#039;, $params);
```

This automatically handles escaping of values, including proper handling of values containing quotes.

## Context Nodes

You can limit the scope of your query by providing a context node:

```php
// Get a context node.
$contextNode = $query-&gt;getNodes(&#039;//section&#039;)-&gt;item(0);

// Query within that context.
$result = $query-&gt;get(&#039;./item&#039;, [], $contextNode);
```

## Return Value Handling

The `get()` method is intelligent about what it returns:

1. `null` if no matching nodes are found.
2. A string if a single node with no children is found.
3. An array if multiple nodes are found.
4. A structured array if nodes with child elements are found.

### Example of Structured Results

For XML like:
```xml
&lt;root&gt;
  &lt;person&gt;
    &lt;name&gt;John&lt;/name&gt;
    &lt;age&gt;30&lt;/age&gt;
  &lt;/person&gt;
  &lt;person&gt;
    &lt;name&gt;Jane&lt;/name&gt;
    &lt;age&gt;25&lt;/age&gt;
  &lt;/person&gt;
&lt;/root&gt;
```

The query `$query-&gt;get(&#039;/root/person&#039;)` would return:
```php
[
    [
        &#039;name&#039; =&gt; &#039;John&#039;,
        &#039;age&#039; =&gt; &#039;30&#039;
    ],
    [
        &#039;name&#039; =&gt; &#039;Jane&#039;,
        &#039;age&#039; =&gt; &#039;25&#039;
    ]
]
```

## Special Features

### Using Without Namespaces

If you need to query XML with namespaces but don&#039;t want to specify them all, you can use the class without registering namespaces:

```php
$query = new XPathQuery($xmlString); // No namespaces registered.

// This will match any element named &#039;Element&#039; regardless of its namespace.
$result = $query-&gt;get(&#039;//Element&#039;);
```

### Value Quoting

The class handles the quoting of values in XPath expressions automatically, even when values contain both single and double quotes:

```php
$params = [&#039;complex&#039; =&gt; &#039;Value with &quot;double&quot; and \&#039;single\&#039; quotes&#039;];
$result = $query-&gt;get(&#039;//element[text()=:complex]&#039;, $params);
```

### Error Handling

The class provides clear error messages when there are issues with the XML document or XPath expressions:

```php
try {
    $result = $query-&gt;get(&#039;//invalid[xpath&#039;);
} catch (InvalidArgumentException $e) {
    // Handle the error.
}
```




---

## XML Service

XmlService Class

# XmlService Class

The `XmlService` class is the central component of the Derafu XML library, providing a unified interface for encoding, decoding, and validating XML documents. It follows a service-oriented architecture by delegating the actual work to specialized components.

## Service Structure

The `XmlService` relies on three key components:

1. **XmlEncoder**: Converts PHP arrays to XML documents.
2. **XmlDecoder**: Converts XML documents to PHP arrays.
3. **XmlValidator**: Validates XML documents against XSD schemas.

## Basic Usage

```php
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);
```

## Encoding (Array to XML)

The `encode()` method converts a PHP array to an XML document:

```php
$data = [
    &#039;invoice&#039; =&gt; [
        &#039;@attributes&#039; =&gt; [
            &#039;id&#039; =&gt; &#039;12345&#039;,
            &#039;date&#039; =&gt; &#039;2025-03-05&#039;
        ],
        &#039;client&#039; =&gt; [
            &#039;name&#039; =&gt; &#039;Acme Corporation&#039;,
            &#039;tax_id&#039; =&gt; &#039;123456789&#039;
        ],
        &#039;items&#039; =&gt; [
            &#039;item&#039; =&gt; [
                [
                    &#039;@attributes&#039; =&gt; [&#039;id&#039; =&gt; &#039;1&#039;],
                    &#039;description&#039; =&gt; &#039;Product A&#039;,
                    &#039;quantity&#039; =&gt; &#039;2&#039;,
                    &#039;price&#039; =&gt; &#039;19.99&#039;
                ],
                [
                    &#039;@attributes&#039; =&gt; [&#039;id&#039; =&gt; &#039;2&#039;],
                    &#039;description&#039; =&gt; &#039;Product B&#039;,
                    &#039;quantity&#039; =&gt; &#039;1&#039;,
                    &#039;price&#039; =&gt; &#039;29.99&#039;
                ]
            ]
        ],
        &#039;total&#039; =&gt; &#039;69.97&#039;
    ]
];

$xmlDocument = $xmlService-&gt;encode($data);
```

This produces:

```xml
&lt;?xml version=&quot;1.0&quot; encoding=&quot;ISO-8859-1&quot;?&gt;
&lt;invoice id=&quot;12345&quot; date=&quot;2025-03-05&quot;&gt;
  &lt;client&gt;
    &lt;name&gt;Acme Corporation&lt;/name&gt;
    &lt;tax_id&gt;123456789&lt;/tax_id&gt;
  &lt;/client&gt;
  &lt;items&gt;
    &lt;item id=&quot;1&quot;&gt;
      &lt;description&gt;Product A&lt;/description&gt;
      &lt;quantity&gt;2&lt;/quantity&gt;
      &lt;price&gt;19.99&lt;/price&gt;
    &lt;/item&gt;
    &lt;item id=&quot;2&quot;&gt;
      &lt;description&gt;Product B&lt;/description&gt;
      &lt;quantity&gt;1&lt;/quantity&gt;
      &lt;price&gt;29.99&lt;/price&gt;
    &lt;/item&gt;
  &lt;/items&gt;
  &lt;total&gt;69.97&lt;/total&gt;
&lt;/invoice&gt;
```

### Using Namespaces

You can specify XML namespaces during encoding:

```php
$namespace = [&#039;http://example.com/invoice&#039;, &#039;inv&#039;];
$xmlDocument = $xmlService-&gt;encode($data, $namespace);
```

This generates:

```xml
&lt;?xml version=&quot;1.0&quot; encoding=&quot;ISO-8859-1&quot;?&gt;
&lt;inv:invoice id=&quot;12345&quot; date=&quot;2025-03-05&quot; xmlns:inv=&quot;http://example.com/invoice&quot;&gt;
  &lt;!-- Content with namespace prefixes --&gt;
&lt;/inv:invoice&gt;
```

## Decoding (XML to Array)

The `decode()` method converts an XML document to a PHP array:

```php
// Assuming $xmlDocument is a Derafu\Xml\XmlDocument instance.
$array = $xmlService-&gt;decode($xmlDocument);

// Or starting from a DOMElement.
$element = $xmlDocument-&gt;getDocumentElement();
$array = $xmlService-&gt;decode($element);
```

### Handling Repeated Elements

By default, elements with the same name are collected into arrays:

```xml
&lt;root&gt;
  &lt;item&gt;Value 1&lt;/item&gt;
  &lt;item&gt;Value 2&lt;/item&gt;
  &lt;item&gt;Value 3&lt;/item&gt;
&lt;/root&gt;
```

Becomes:

```php
[
    &#039;root&#039; =&gt; [
        &#039;item&#039; =&gt; [
            &#039;Value 1&#039;,
            &#039;Value 2&#039;,
            &#039;Value 3&#039;
        ]
    ]
]
```

You can control this behavior with the `$twinsAsArray` parameter:

```php
$array = $xmlService-&gt;decode($xmlDocument, null, twinsAsArray: true);
```

## Validation

The `validate()` method checks an XML document against an XSD schema:

```php
use Derafu\Xml\Exception\XmlException;

try {
    $xmlService-&gt;validate($xmlDocument, &#039;/path/to/schema.xsd&#039;);
    echo &quot;XML is valid!&quot;;
} catch (XmlException $e) {
    echo &quot;Validation failed: &quot; . $e-&gt;getMessage();

    // Get detailed error information.
    $errors = $e-&gt;getErrors();
    foreach ($errors as $error) {
        echo &quot;- $error\n&quot;;
    }
}
```

### Schema Auto-Detection

If the XML document includes a `schemaLocation` attribute, you can omit the schema path:

```php
// XML with xsi:schemaLocation=&quot;http://example.com/ns schema.xsd&quot;
try {
    $xmlService-&gt;validate($xmlDocument); // Will use schema.xsd
} catch (XmlException $e) {
    // Handle validation error.
}
```

### Error Translations

The validator can translate technical libxml error messages into more user-friendly messages:

```php
$translations = [
    &#039;element1&#039; =&gt; &#039;Customer Info&#039;,
    &#039;element2&#039; =&gt; &#039;Product Details&#039;
];

try {
    $xmlService-&gt;validate($xmlDocument, &#039;/path/to/schema.xsd&#039;, $translations);
} catch (XmlException $e) {
    // Error messages will use the translated element names.
}
```

## Integration Example

This complete example shows how to use the XML service for a typical workflow:

```php
// Initialize the service.
$encoder = new XmlEncoder();
$decoder = new XmlDecoder();
$validator = new XmlValidator();
$xmlService = new XmlService($encoder, $decoder, $validator);

// Create XML from array.
$data = [&#039;root&#039; =&gt; [&#039;element&#039; =&gt; &#039;value&#039;]];
$xmlDocument = $xmlService-&gt;encode($data);

// Save to a file.
file_put_contents(&#039;document.xml&#039;, $xmlDocument-&gt;saveXml());

// Load from a file.
$loadedXml = new \Derafu\Xml\XmlDocument();
$loadedXml-&gt;loadXml(file_get_contents(&#039;document.xml&#039;));

// Validate.
try {
    $xmlService-&gt;validate($loadedXml, &#039;schema.xsd&#039;);

    // Convert back to array.
    $array = $xmlService-&gt;decode($loadedXml);

    // Process the data.
    // ...

} catch (\Derafu\Xml\Exception\XmlException $e) {
    // Handle validation errors.
}
```





---
Last updated on 24/08/2026
#php
