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

# Derafu UBL



---

## Introduction

Typed PHP model of UBL 2.1 (Universal Business Language) to read, write and validate business documents against the official XSD

# One Language for Every Business Document

[![GitHub](https://img.shields.io/badge/github-derafu%2Fubl-blue?logo=github)](https://github.com/derafu/ubl)
![GitHub last commit](https://img.shields.io/github/last-commit/derafu/ubl/main)
![CI Workflow](https://github.com/derafu/ubl/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/ubl)
![GitHub Issues](https://img.shields.io/github/issues-raw/derafu/ubl)
![Total Downloads](https://poser.pugx.org/derafu/ubl/downloads)
![Monthly Downloads](https://poser.pugx.org/derafu/ubl/d/monthly)

Derafu UBL is a typed PHP model of [UBL](https://docs.oasis-open.org/ubl/) (Universal Business
Language): one PHP class per document type and per building block defined by the official UBL
2.1 XSD, with services to read and write those documents in multiple formats and validate them
against the real schema.

It is deliberately generic: nothing in it knows about any one business (no fixed seller, no
fixed tax rate, no fixed currency) and nothing about any e-invoicing profile (no Peppol rules,
no country-specific mandatory fields or codelists). That kind of logic belongs in a package
built on top of this one, for whichever network or country it targets.

## Features

- **One class per UBL 2.1 type**: every document (`Quotation`, `Invoice`, `CreditNote`, ...)
  and every shared building block (`Party`, `MonetaryTotal`, `TaxTotal`, every `*Line` type) as
  a plain, typed PHP class.
- **Three wire formats**: serialize and deserialize the same document as `json`, `yaml` or
  `xml`, through one consistent API.
- **Schema validation**: validate a document&#039;s XML against the official OASIS UBL 2.1 XSD
  bundled with the package.
- **Array/JSON native**: every class also has `toArray()`/`fromArray()`/`jsonSerialize()`, for
  working with a document as plain data with no XML involved at all.

## Installation

```bash
composer require derafu/ubl
```

### Applying the Bundled Patch to xterr/php-ubl

`derafu/ubl` wraps `xterr/php-ubl` for `xml`, and ships a fix for a few of its known defects
as a script that patches it directly in `vendor/` (`tools/patch-xterr-ubl.php`). That script
is wired as `derafu/ubl`&#039;s own Composer `post-install-cmd`/`post-update-cmd`, so it reapplies
automatically whenever `derafu/ubl` itself is installed or updated as the root project.

Composer does not run a dependency&#039;s own scripts for the project that requires it - only the
root project&#039;s own `composer.json` scripts run. A project that depends on `derafu/ubl` must
add the same command to its own `composer.json` to actually get the patch; otherwise it never
runs, and `xterr/php-ubl` keeps its original, unpatched behavior silently.

`post-install-cmd`/`post-update-cmd` each accept either a single command or an array of them -
use the array form, so this is added alongside whatever else is already there instead of
replacing it:

```json
{
    &quot;scripts&quot;: {
        &quot;post-install-cmd&quot;: [
            &quot;php vendor/derafu/ubl/tools/patch-xterr-ubl.php&quot;
        ],
        &quot;post-update-cmd&quot;: [
            &quot;php vendor/derafu/ubl/tools/patch-xterr-ubl.php&quot;
        ]
    }
}
```

Run `composer install` (or `composer update`) once more after adding it, so the patch also
applies to the `xterr/php-ubl` already sitting in `vendor/`.

## Basic Usage

### Building a Document

Using an object-oriented API:

```php
use Derafu\Ubl\Oasis\Cac\Item;
use Derafu\Ubl\Oasis\Cac\LineItem;
use Derafu\Ubl\Oasis\Cac\MonetaryTotal;
use Derafu\Ubl\Oasis\Cac\Party;
use Derafu\Ubl\Oasis\Cac\PartyName;
use Derafu\Ubl\Oasis\Cac\Price;
use Derafu\Ubl\Oasis\Cac\QuotationLine;
use Derafu\Ubl\Oasis\Cac\SupplierParty;
use Derafu\Ubl\Oasis\Cbc\Amount;
use Derafu\Ubl\Oasis\Cbc\Identifier;
use Derafu\Ubl\Oasis\Cbc\Text;
use Derafu\Ubl\Oasis\Doc\Quotation;

$quotation = (new Quotation())
    -&gt;setId((new Identifier())-&gt;setValue(&#039;Q-001&#039;))
    -&gt;setIssueDate(new DateTimeImmutable(&#039;2026-01-15&#039;))
    -&gt;setSellerSupplierParty(
        (new SupplierParty())-&gt;setParty(
            (new Party())-&gt;setPartyNames([
                (new PartyName())-&gt;setName((new Text())-&gt;setValue(&#039;Acme Corp&#039;)),
            ])
        )
    )
    -&gt;setQuotedMonetaryTotal(
        (new MonetaryTotal())-&gt;setPayableAmount(
            (new Amount())-&gt;setValue(&#039;100&#039;)-&gt;setCurrencyID(&#039;EUR&#039;)
        )
    )
    -&gt;setQuotationLines([
        (new QuotationLine())
            -&gt;setId((new Identifier())-&gt;setValue(&#039;1&#039;))
            -&gt;setLineItem(
                (new LineItem())
                    -&gt;setId((new Identifier())-&gt;setValue(&#039;1&#039;))
                    -&gt;setPrice(
                        (new Price())-&gt;setPriceAmount(
                            (new Amount())-&gt;setValue(&#039;100&#039;)-&gt;setCurrencyID(&#039;EUR&#039;)
                        )
                    )
                    -&gt;setItem((new Item())-&gt;setName((new Text())-&gt;setValue(&#039;Widget&#039;)))
            ),
    ]);
```

Only `ID`, `IssueDate`, `SellerSupplierParty`, `QuotedMonetaryTotal` and one `QuotationLine`
(with `LineItem.ID` and `LineItem.Item`) are actually `required: true` in UBL - an `Item` with
no `Name`, and a line with no `Price`, are schema-valid too. The seller&#039;s and item&#039;s names, and
the price, are kept here only so the example reads like a real quotation, the same choice
behind this package&#039;s own committed `Quotation` fixture.

Or using a plain PHP array - the exact same shape `toArray()`/`jsonSerialize()` produce, and
`json`/`yaml` wrap under the document type&#039;s name (see Formats):

```php
use Derafu\Ubl\Oasis\Doc\Quotation;

$quotation = Quotation::fromArray([
    &#039;ID&#039; =&gt; &#039;Q-001&#039;,
    &#039;IssueDate&#039; =&gt; &#039;2026-01-15&#039;,
    &#039;SellerSupplierParty&#039; =&gt; [
        &#039;Party&#039; =&gt; [
            &#039;PartyName&#039; =&gt; [
                [&#039;Name&#039; =&gt; &#039;Acme Corp&#039;],
            ],
        ],
    ],
    &#039;QuotedMonetaryTotal&#039; =&gt; [
        &#039;PayableAmount&#039; =&gt; [
            &#039;@value&#039; =&gt; &#039;100&#039;,
            &#039;@attributes&#039; =&gt; [&#039;currencyID&#039; =&gt; &#039;EUR&#039;],
        ],
    ],
    &#039;QuotationLine&#039; =&gt; [
        [
            &#039;ID&#039; =&gt; &#039;1&#039;,
            &#039;LineItem&#039; =&gt; [
                &#039;ID&#039; =&gt; &#039;1&#039;,
                &#039;Price&#039; =&gt; [
                    &#039;PriceAmount&#039; =&gt; [
                        &#039;@value&#039; =&gt; &#039;100&#039;,
                        &#039;@attributes&#039; =&gt; [&#039;currencyID&#039; =&gt; &#039;EUR&#039;],
                    ],
                ],
                &#039;Item&#039; =&gt; [
                    &#039;Name&#039; =&gt; &#039;Widget&#039;,
                ],
            ],
        ],
    ],
]);
```

### Serializing and Deserializing

```php
use Derafu\Ubl\Service\Deserializer;
use Derafu\Ubl\Service\Serializer;

$serializer = new Serializer();
$deserializer = new Deserializer();

$yaml = $serializer-&gt;serialize($quotation, &#039;yaml&#039;);
$json = $serializer-&gt;serialize($quotation, &#039;json&#039;);
$xml = $serializer-&gt;serialize($quotation, &#039;xml&#039;);

$sameQuotation = $deserializer-&gt;deserialize($yaml, &#039;yaml&#039;);
```

See **Formats** for the exact shape of each one.

### Validating Against the Official Schema

```php
use Derafu\Ubl\Exception\ValidationException;
use Derafu\Ubl\Service\SchemaValidator;

try {
    (new SchemaValidator())-&gt;validate($xml);
} catch (ValidationException $e) {
    foreach ($e-&gt;getErrors() as $error) {
        echo $error-&gt;message, &quot;\n&quot;;
    }
}
```

See **Validation** for how the document type is resolved and what else can be inspected on a
failure.

## What This Package Is Not

- **Not a profile.** PEPPOL BIS, EN 16931, and any country&#039;s CIUS (Core Invoice Usage
  Specification) restrict or extend this base syntax with their own mandatory fields,
  codelists and Schematron business rules. Those are separate packages that depend on this
  one, never the other way around.
- **Not a mapping layer.** Turning your own application&#039;s data (prices, customers, products)
  into one of these documents is the job of your application, not of this package.




---

## Document Types

The 65 UBL 2.1 document types this package models, one PHP class per document

# Document Types

Every document type defined by UBL 2.1 is available as a class under `Derafu\Ubl\Oasis\Doc\`,
named exactly like the document (`Quotation`, `Invoice`, `CreditNote`, ...). There is no
sub-namespace per business domain: most of the underlying building blocks (`Party`,
`MonetaryTotal`, `TaxTotal`, every `*Line` type) are shared across nearly all of them
regardless of domain, and a document does not belong to a single domain either (an `Invoice`
is billing, but it is referenced from other flows through `BillingReference`).

## Available Documents

| Class | Definition |
|---|---|
| `ApplicationResponse` | A document to indicate the application&#039;s response to a transaction. |
| `AttachedDocument` | A wrapper that allows a document of any kind to be packaged with the UBL document that references it. |
| `AwardedNotification` | The document used to communicate a contract award to the winner. |
| `BillOfLading` | A document issued by the party who acts as an agent for a transportation carrier or other agents to the party who gives instructions for the transportation services (shipper, consignor, etc.) stating the details of the transportation, charges, and terms and conditions under which the transportation service is provided. |
| `CallForTenders` | A document used by a Contracting Party to define a procurement project to buy goods, services, or works during a specified period. |
| `Catalogue` | A document that describes items, prices, and price validity. |
| `CatalogueDeletion` | A document used to cancel an entire Catalogue. |
| `CatalogueItemSpecificationUpdate` | A document used to update information (e.g., technical descriptions and properties) about Items in an existing Catalogue. |
| `CataloguePricingUpdate` | A document used to update information about prices in an existing Catalogue. |
| `CatalogueRequest` | A document used to request a Catalogue. |
| `CertificateOfOrigin` | A document that describes the Certificate of Origin. |
| `ContractAwardNotice` | A document published by a Contracting Party to announce the awarding of a procurement project. |
| `ContractNotice` | A document used by a Contracting party to announce a project to buy goods, services, or works. |
| `CreditNote` | A document used to specify credits due to the Debtor from the Creditor. |
| `DebitNote` | A document used to specify debts incurred by the Debtor. |
| `DespatchAdvice` | A document used to describe the despatch or delivery of goods and services. |
| `DocumentStatus` | A document used to provide information about document status. |
| `DocumentStatusRequest` | A document used to request the status of another document. |
| `ExceptionCriteria` | A document used to specify the thresholds for forecast variance, product activity, and performance history beyond which exceptions should be triggered. |
| `ExceptionNotification` | A document used to notify an exception in forecast variance, product activity, or performance history. |
| `Forecast` | A document used to forecast sales or orders. |
| `ForecastRevision` | A document used to revise a Forecast. |
| `ForwardingInstructions` | A document issued to a forwarder, giving instructions regarding the action to be taken for the forwarding of goods described therein. |
| `FreightInvoice` | A document stating the charges incurred for a logistics service. |
| `FulfilmentCancellation` | A document used to cancel an entire fulfilment document (Despatch Advice or Receipt Advice). |
| `GoodsItemItinerary` | A document providing details relating to a transport service, such as transport movement, identification of equipment and goods, subcontracted service providers, etc. |
| `GuaranteeCertificate` | A document to notify the deposit of a bid bond guarantee. |
| `InstructionForReturns` | A document used to initiate a return of goods. |
| `InventoryReport` | A report on the quantities of each item that are, or will be, in stock. |
| `Invoice` | A document used to request payment. |
| `ItemInformationRequest` | A document used to request product activity, forecast, or performance data. |
| `Order` | A document used to order goods and services. |
| `OrderCancellation` | A document used to cancel an entire Order. |
| `OrderChange` | A document used to specify changes to an existing Order. |
| `OrderResponse` | A document used to indicate detailed acceptance or rejection of an Order or to make a counter-offer. |
| `OrderResponseSimple` | A document used to indicate simple acceptance or rejection of an entire Order. |
| `PackingList` | A document describing how goods are packed. |
| `PriorInformationNotice` | A document used by a contracting party to declare the intention to buy goods, services, or works during a specified period. |
| `ProductActivity` | A document reporting the movement of goods at specified retail locations for inventory tracking purposes. |
| `Quotation` | A document used to quote for the provision of goods and services. |
| `ReceiptAdvice` | A document used to describe the receipt of goods and services. |
| `Reminder` | A document used to remind a customer of payments past due. |
| `RemittanceAdvice` | A document that specifies details of an actual payment. |
| `RequestForQuotation` | A document used to request a Quotation for goods and services from a Seller. |
| `RetailEvent` | A document used to specify basic information about retail events (such as promotions, product introductions, and community or environmental events) that affect supply or demand. |
| `SelfBilledCreditNote` | A credit note created by the debtor in a self billing arrangement with a creditor; Self Billed Credit Note replaces Debit Note in such arrangements. |
| `SelfBilledInvoice` | An Invoice document created by the Customer (rather than the Supplier) in a Self Billing relationship. |
| `Statement` | A document used to report the status of orders, billing, and payment. |
| `StockAvailabilityReport` | A report on the quantities of each item that are, or will be, in stock. |
| `Tender` | A document whereby an economic operator (the tenderer) makes a formal offer (the tender) to a contracting authority to execute an order for the supply or purchase of goods, or for the execution of work, according to the terms of a proposed contract. |
| `TenderReceipt` | A document sent by a contracting party to an economic operator acknowledging receipt of a Tender. |
| `TendererQualification` | A document declaring the qualifications of a tenderer. |
| `TendererQualificationResponse` | A document issued by a procurement organization to notify an economic operator whether it has been admitted to or excluded from the tendering process. |
| `TradeItemLocationProfile` | A document specifying trade item attributes relating to replenishment policies. |
| `TransportExecutionPlan` | A document used in the negotiation of a transport service between a transport user and a transport service provider. |
| `TransportExecutionPlanRequest` | A document sent by a transport user to request a transport service from a transport service provider. |
| `TransportProgressStatus` | A document sent from a Transportation Network Manager to a Transport Service Provider giving the status of the whereabouts and schedule of the transport means involved in a transport service. |
| `TransportProgressStatusRequest` | A document sent from a transport service provider to a transportation network manager requesting a Transport Progress Status. |
| `TransportServiceDescription` | A document sent by a transport service provider to announce the availability of a transport service. |
| `TransportServiceDescriptionRequest` | A document requesting a Transport Service Description, sent from a party with a transport demand (transport user) to a party providing transport services (transport service provider). |
| `TransportationStatus` | A document to circulate reports of transportation status or changes in status (events) among a group of participants. |
| `TransportationStatusRequest` | A document requesting a Transportation Status report. |
| `UnawardedNotification` | A document communicating to a tenderer that the contract has been awarded to different tenderer. |
| `UtilityStatement` | A supplement to an Invoice or Credit Note, containing information on the consumption of services provided by utility suppliers to private and public customers, including electricity, gas, water, and telephone services. |
| `Waybill` | A transport document describing a shipment. |

## Working with a Document

A document is a plain object: build it with its setters, or hydrate one from data with
`fromArray()`. See **Introduction** for a complete example, and **Formats** for reading and
writing documents as `json`, `yaml` or `xml`.




---

## Formats

Reading and writing a UBL document as json, yaml or xml with Serializer and Deserializer

# Formats

`Serializer` and `Deserializer` turn a document into a string and back, across three formats:
`json`, `yaml` and `xml`. Both resolve the right format handler by name internally, so the
same two classes are the only thing a consumer ever needs, regardless of format.

## Supported Formats

| Format | Dependency | Notes |
|---|---|---|
| `json` | PHP built-in | Always available. |
| `yaml` | `symfony/yaml` | Optional: see Optional Dependencies below. |
| `xml` | `xterr/php-ubl` | The real UBL XML, checked by **Validation**. |

## Serializing a Document

```php
use Derafu\Ubl\Service\Serializer;

$serializer = new Serializer();

$yaml = $serializer-&gt;serialize($quotation, &#039;yaml&#039;);
$json = $serializer-&gt;serialize($quotation, &#039;json&#039;);
$xml = $serializer-&gt;serialize($quotation, &#039;xml&#039;);
```

## Deserializing a Document

```php
use Derafu\Ubl\Service\Deserializer;

$deserializer = new Deserializer();

$quotation = $deserializer-&gt;deserialize($yaml, &#039;yaml&#039;);
```

An explicit target class can be passed as a third argument to skip or override the automatic
detection described below:

```php
use Derafu\Ubl\Oasis\Doc\Quotation;

$quotation = $deserializer-&gt;deserialize($yaml, &#039;yaml&#039;, Quotation::class);
```

## Self-Describing Content

Neither `json` nor `yaml` needs an explicit target class to know what to deserialize into: the
single top-level key of the decoded content names the document type directly, e.g.
`{&quot;Quotation&quot;: {...}}` or `Quotation:` at the top of a YAML file. `xml` works the same way
through its own mechanism: the document type is read from the XML root element
(`&lt;Quotation xmlns=&quot;...:Quotation-2&quot;&gt;`).

`Serializer::serialize()` always wraps `json`/`yaml` output under that same top-level key
(never a bare array), so what it returns for one format is exactly what `Deserializer::
deserialize()` accepts back for that same format.

## Value and Attribute Representation

Every class also has `toArray()`/`fromArray()`/`jsonSerialize()`, used internally by the `json`
and `yaml` handlers and available directly to a consumer who wants a plain array with no
format involved:

```php
$array = $quotation-&gt;toArray();
$quotation = Quotation::fromArray($array);
```

Keys are the UBL element and attribute names, not PHP property names. A value with no
attributes is a plain scalar:

```php
[&#039;ID&#039; =&gt; &#039;Q-001&#039;, &#039;IssueDate&#039; =&gt; &#039;2026-01-15&#039;]
```

A value that carries an attribute, like an amount&#039;s currency, uses the `@value`/`@attributes`
form instead:

```php
[
    &#039;@value&#039; =&gt; &#039;100&#039;,
    &#039;@attributes&#039; =&gt; [&#039;currencyID&#039; =&gt; &#039;EUR&#039;],
]
```

## Optional Dependencies

`symfony/yaml` is not a hard dependency of this package: the `yaml` handler is only autoloaded
the first time a caller actually serializes or deserializes with `&#039;yaml&#039;`. A consumer who never
touches that format never needs it installed; one who does gets PHP&#039;s own &quot;Class not found&quot;
error if it is missing, with no extra exception type or `class_exists()` guard in between.




---

## Validation

Validating a UBL document&#039;s XML against the official OASIS UBL 2.1 XSD

# Validation

`SchemaValidator` validates a document&#039;s XML directly against the official OASIS UBL 2.1 XSD
bundled with the package - the same schema real UBL consumers (Peppol Access Points, tax
authorities) validate against.

## Validating a Document

```php
use Derafu\Ubl\Service\SchemaValidator;

$validator = new SchemaValidator();

$validator-&gt;validate($xml); // Throws Derafu\Ubl\Exception\ValidationException if invalid.
```

Nothing is returned on success: a document either validates, or the call throws.

## Resolving the Document Type

Which XSD file to validate against is resolved automatically from the XML&#039;s own root element,
the same way `Deserializer` resolves a target class for `xml` (see Self-Describing Content in
**Formats**). An explicit class can be passed as a second argument to skip or override that:

```php
use Derafu\Ubl\Oasis\Doc\Quotation;

$validator-&gt;validate($xml, Quotation::class);
```

## Handling Validation Errors

A failed validation throws `Derafu\Ubl\Exception\ValidationException`, which exposes the real
list of `LibXMLError` objects libxml reported, not only a flat message:

```php
use Derafu\Ubl\Exception\ValidationException;

try {
    $validator-&gt;validate($xml);
} catch (ValidationException $e) {
    foreach ($e-&gt;getErrors() as $error) {
        printf(&quot;Line %d: %s\n&quot;, $error-&gt;line, trim($error-&gt;message));
    }
}
```

A malformed XML string, or a well-formed one whose root element matches no known UBL
document, also throws `ValidationException` - with `getErrors()` empty in both cases, since
neither one ever reaches schema validation itself: the document type could not be resolved to
begin with.




---

## Services

The services this package provides, their interfaces, exceptions, and how to wire them with Symfony&#039;s dependency injection

# Services

## Available Services

| Interface | Class | Purpose |
|---|---|---|
| `Contract\SerializerInterface` | `Service\Serializer` | Turn a document into a `json`/`yaml`/`xml` string. |
| `Contract\DeserializerInterface` | `Service\Deserializer` | Turn a `json`/`yaml`/`xml` string into a document. |
| `Contract\ValidatorInterface` | `Service\SchemaValidator` | Validate a document&#039;s XML against the official XSD. |
| `Contract\FormatHandlerFactoryInterface` | `Service\FormatHandlerFactory` | Resolve a format name to the handler that implements it; register a custom one. |

Every class is namespaced under `Derafu\Ubl\`. See **Formats** for `Serializer`/`Deserializer`
usage and **Validation** for `SchemaValidator`.

## Exceptions

Every exception this package throws extends `Derafu\Ubl\Exception\UblException`, itself a
`Derafu\Translation\Exception\Core\TranslatableException`:

| Exception | Thrown when |
|---|---|
| `UnsupportedFormatException` | A format name is not `json`, `yaml` or `xml` (or a custom registered one). |
| `SerializationException` | A document cannot be turned into the requested format. |
| `DeserializationException` | Content cannot be turned into a document (invalid JSON/YAML, an unknown document type, ...). |
| `ValidationException` | A document&#039;s XML does not validate against the schema. Carries the real `LibXMLError` list, see **Validation**. |

Catching the base `UblException` catches any of them:

```php
use Derafu\Ubl\Exception\UblException;

try {
    $document = (new Deserializer())-&gt;deserialize($content, &#039;yaml&#039;);
} catch (UblException $e) {
    // SerializationException, DeserializationException, ValidationException, ...
}
```

## Dependency Injection

For an application using `symfony/dependency-injection`, `resources/config/ubl-services.yaml`
wires every service above to its interface, ready to autowire:

```yaml
imports:
    - { resource: &#039;../vendor/derafu/ubl/resources/config/ubl-services.yaml&#039; }
```

With that imported, a service can simply type-hint the interface it needs:

```php
use Derafu\Ubl\Contract\SerializerInterface;

final class QuotationController
{
    public function __construct(
        private readonly SerializerInterface $serializer,
    ) {
    }
}
```





---
Last updated on 12/10/2026
#php
