Map strongly-typed PHP objects to XML using Symfony's PropertyInfo and TypeInfo components.
XmlMapper is a PHP library that maps strongly-typed PHP objects (DTOs, value objects, entities) to XML using reflection and PHPDoc annotations. It leverages Symfony's PropertyInfo and TypeInfo components to derive each property's type and render a matching XML document.
| Key | Value |
|---|---|
| Package | magicsunday/xmlmapper |
| PHP | ^8.3 |
| Main API | MagicSunday\XmlEncoder |
| Output | XML string (string); false only if serialization itself fails |
XmlMapper takes a PHP object implementing MagicSunday\XmlSerializable and renders it as XML, including nested objects, scalar and object collections, and custom types. Property values are routed to elements, attributes, raw text nodes or CDATA sections via a small set of annotations, and property names can be converted on the fly (e.g. snake_case to camelCase).
Serializing domain objects to XML usually means hand-writing DOMDocument/XMLWriter boilerplate that drifts from the underlying model. XmlMapper derives the XML structure from the object's typed properties and a few annotations, so the output follows the PHP model, with explicit hooks (attributes, CDATA, node values, custom type closures) where you need to deviate.
composer require magicsunday/xmlmapperAnnotate the classes you want to serialize and let them implement XmlSerializable:
namespace App\Model;
use MagicSunday\XmlSerializable;
use MagicSunday\XmlMapper\Annotation\XmlAttribute;
final class Author implements XmlSerializable
{
public string $name = 'Jane Doe';
}
final class Book implements XmlSerializable
{
#[XmlAttribute]
public string $isbn = '978-3-16-148410-0';
public string $title = 'The Title';
public Author $author;
/**
* @var string[]
*/
public array $tags = ['php', 'xml'];
}Build an encoder and map an instance:
require __DIR__ . '/vendor/autoload.php';
use App\Model\Author;
use App\Model\Book;
use MagicSunday\XmlEncoder;
use MagicSunday\XmlMapper\Converter\CamelCasePropertyNameConverter;
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
use Symfony\Component\PropertyInfo\PropertyInfoExtractor;
$extractor = new PropertyInfoExtractor(
[new ReflectionExtractor()],
// PhpDocExtractor resolves `@var` generics such as `Chapter[]`;
// ReflectionExtractor covers native types, so an array property without a
// docblock is still recognised as a collection instead of being silently
// rendered as one empty element.
[new PhpDocExtractor(), new ReflectionExtractor()]
);
$book = new Book();
$book->author = new Author();
$encoder = new XmlEncoder($extractor, new CamelCasePropertyNameConverter());
echo $encoder->map($book);<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<book isbn="978-3-16-148410-0">
<title>The Title</title>
<author>
<name>Jane Doe</name>
</author>
<tags>php</tags>
<tags>xml</tags>
</book>The name converter is optional; without it the raw class and property names are used verbatim. For collections, annotate the property with the phpDocumentor collection type so the value type can be resolved:
/** @var string[] $tags */
/** @var Chapter[] $chapters */
/** @var array<int, Author|Chapter> $members */Each marker is applied as a native PHP attribute (shown above).
| Marker | Effect |
|---|---|
XmlAttribute |
Render the value as an attribute of the surrounding element. |
XmlNodeValue |
Render the value as the raw text content of the surrounding element. |
XmlCDataSection |
Wrap the value in a <![CDATA[ β¦ ]]> section (markup left intact). |
Register a closure to transform every value of a given class or builtin type (Money::class, bool, int, array, object, β¦) before it is written:
$encoder->addType('bool', static fn (string $name, mixed $value): string => $value === true ? 'yes' : 'no');- API reference
- Recipes
- Manual instantiation β wiring the Symfony extractor and name converter
- Markers: attributes, node values and CDATA β native attribute syntax
- Custom types β transforming values with
addType() - Custom name converter β element naming
- Collections β scalar, object, nullable and union-typed collections
Prerequisites:
- PHP
^8.3 - Extensions:
dom,xml - Node.js (for the copy-paste detection gate, run via
npx)
Install dependencies:
composer installRun the mandatory quality gate:
composer ci:testci:test includes:
- Linting (
phplint) - Unit tests (
phpunit) - Static analysis (
phpstan, max level) - Refactoring dry-run (
rector --dry-run) - Coding standards dry-run (
php-cs-fixer --dry-run) - Copy-paste detection (
jscpd)
Contributions are welcome. Please run the full composer ci:test quality gate before submitting a pull request, and keep changes covered by tests.