As you might know programming languages like .NET or Java use attributes to serialize Data Transfer Objects (DTO) to valid XML. This was not able with PHP itself so far.
Until now, complex, error-prone serializers have been used that often worked only for a specific use case. These were then fed with complex mapping configurations in a wide variety of formats (YAML, XML, JSON). The result was often convoluted DOM manipulation that was difficult to understand. Furthermore, there was a lack of type safety, IDE support, and ease of maintenance.
What This Repository Enables
The project introduces XML attributes for PHP data objects—similar to .NET and Java.
This allows you to:
- Define XML structures directly on the Data Transfer Object.
- Control serialization and deserialization in a type-safe manner.
- Model complex XML schemas without external mapping files.
- Significantly improve the readability and maintainability of your DTOs
In short: PHP is finally getting a feature that has been standard in enterprise languages for years.
XML Leafs / Simple Content Types
A so-called XML leaf or simple content type is a node that contains no child elements in the XML tree and consists solely of simple values, such as text, numbers, or dates. Depending on the scope of the XML data model to be used, various simple content types can therefore be defined.
<xsd:complexType name="DateType">
<xsd:simpleContent>
<xsd:extension base="xsd:date"/>
</xsd:simpleContent>
</xsd:complexType>
The example shown looks like this:
use MMNewmedia\Model\ValueableInterface;
use MMNewmedia\Model\ValueableTrait;
use MMNewmedia\Xsd;
#[Xsd\SimpleContent]
#[Xsd\Extension(base: 'xsd:date')]
class DateType implements ValueableInterface
{
use ValueableTrait;
}
Attributes for XML Leafs
Of course, a Simple Content Type like this can also have attributes. Using simple PHP property promotion and the #[Xsd\Attribute] attribute, properties of a DTO can be declared as XML attributes.
<xsd:complexType name="AmountType">
<xsd:simpleContent>
<xsd:extension base="xsd:decimal">
<xsd:attribute name="currencyID" type="xsd:token"/>
<xsd:attribute name="currencyCodeListVersionID" type="xsd:token"/>
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
Your DTO would look like this:
use MMNewmedia\Model\ValueableInterface;
use MMNewmedia\Model\ValueableTrait;
use MMNewmedia\Xsd;
#[Xsd\SimpleContent]
#[Xsd\Extension(base: 'xsd:decimal')]
final class AmountType implements ValueableInterface
{
use ValueableTrait;
public function __construct(
#[Xsd\Attribute(
name: 'currencyID',
type: 'xsd:string',
use: Xsd\AttributeUseType::OPTIONAL
)]
public ?string $currencyID = null,
) {}
}
Complex Types
This is where things get really convenient. Once the XML simple types have been defined as PHP DTOs, complex types are a breeze. An XML complex type is, as the name suggests, a complex data type - an XSD element type that can contain other elements and attributes. Unlike simple types, complex types describe complex, nested data. They allow an XML element to have attributes, child elements, or a combination of text and elements.
In a PHP DTO, complex types are annotated with the #[Xsd\ComplexType] attribute. Optionally, the #[Xsd\Schema] attribute can also be used to define a target namespace. The PHP attribute #[Xsd\Sequence] can be used to specify the order of elements within a complex type. Elements that occur n times (e.g., via the XSD attribute maxOccurs="unbounded") are collected in a SplObjectStorage instance.
use MMNewmedia\Xsd;
use SplObjectStorage;
#[Xsd\ComplexType]
#[Xsd\Schema(targetNamespace: 'urn:example:invoice')]
#[Xsd\Element(namespace: HeaderNamespaces::INV, name: 'Invoice', type: InvoiceType::class)]
final readonly class InvoiceType
{
public function __construct(
#[Xsd\Element(namespace: HeaderNamespaces::CBC, name: 'ID', type: IdType::class)]
#[Xsd\Sequence(position: 1)]
public IdType $id,
#[Xsd\Element(name: 'Note', maxOccurs: 99, type: NoteType::class)]
#[Xsd\Sequence(position: 2)]
public SplObjectStorage $note,
) {}
}
Json Serialization
The repository contains a serializer that can convert PHP DTOs into a valid JSON format and generate PHP DTOs from a valid JSON format.
use App\Model\CrossIndustryInvoice\CrossIndustryInvoiceType;
use MMNewmedia\Serializer\JsonDtoSerializer;
$invoice = new CrossIndustryInvoiceType(...);
$serializer = new JsonDtoSerializer();
// DTO -> JSON
$json = $serializer->serialize($invoice);
// JSON -> DTO
$serializer->setRootClasses([
'CrossIndustryInvoice' => CrossIndustryInvoiceType::class,
]);
$invoice = $serializer->unserialize($json);
Simple content types are serialized as scalar values. When a simple content type attribute is declared, its JSON representation becomes an object in which the XML attributes serve as keys and the value is stored under the key value.
Thus, a complete DTO structure can be serialized as valid JSON and used, for example, in REST web services.
XML Serialization
XML serialization is at the heart of this repository. It allows you to generate valid XML from a PHP DTO structure.
use App\Xml\HeaderNamespaces;
use MMNewmedia\Serializer\XmlDtoSerializer;
$serializer = new XmlDtoSerializer();
// map namespace URIs to prefixes via a BackedEnum (case name => xml namespace uris)
$serializer->setNamespaces(HeaderNamespaces::class);
// for model families that declare the namespace per element, prefer element namespaces:
$serializer->setUsePropertyNamespaces(true);
$xml = $serializer->serialize($invoice);
If usePropertyNamespaces is disabled, child elements inherit the targetNamespace of the #[Schema] attribute from the parent complex type; the #[Element] namespaces at the property level serve only as a fallback.
Specify XML Namespaces for Serialization
XML namespaces are passed as PHP enums when the XML serializer is initialized. The serializer class expects a single parameter: a BackedEnum instance that holds the namespace prefix as the enumeration and the corresponding namespace URI as the value.
enum HeaderNamespaces: string
{
case INV = 'urn:example:invoice';
case CBC = 'urn:example:commonbasic';
case CAC = 'urn:example:commonaggregate';
}
The advantage here is that full namespace URIs do not need to be specified in either the #[Schema] or the #[Element] PHP attributes. The enumeration must always include all namespaces to be used.
Conclusion on DTO-XML Serialization
This repository allows complex PHP DTO structures to be mapped to corresponding XML trees. As a developer, you can work exclusively with PHP DTOs that use the attributes provided by this repository, which can then be easily serialized into JSON or XML. This is exactly what other programming languages are already capable of.
XML structures no longer need to be represented with complex DOM manipulations. The benefits of DTOs remain intact. Strict type hinting not only helps in your IDE but also ensures type safety.
Known Issues
- Deserialization from XML to PHP DTO is not implemented yet
- There should be a
#[RootElement]attribute instead of using#[Element]attributes for XML root elements in PHP DTOs - root elements could be found automatically instead of doing it manually - Some PHPDOC comments are still in german language