PHPackages                             john-wink/en16931-php - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Validation &amp; Sanitization](/categories/validation)
4. /
5. john-wink/en16931-php

ActiveLibrary[Validation &amp; Sanitization](/categories/validation)

john-wink/en16931-php
=====================

A dependency-free, Java-free PHP validator for EN 16931 electronic invoices (ZUGFeRD / Factur-X / XRechnung, CII and UBL) — full KoSIT rule coverage in pure PHP.

v0.3.0(1mo ago)020MITPHPPHP ^8.4CI passing

Since Jul 11Pushed 1mo agoCompare

[ Source](https://github.com/john-wink/en16931-php)[ Packagist](https://packagist.org/packages/john-wink/en16931-php)[ Docs](https://github.com/john-wink/en16931-php)[ RSS](/packages/john-wink-en16931-php/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (1)Dependencies (5)Versions (5)Used By (0)

en16931-php
===========

[](#en16931-php)

**A dependency-free, Java-free PHP validator for EN 16931 e-invoices** (ZUGFeRD / Factur-X / XRechnung), in **both CII and UBL syntax**.

The official EN 16931 / KoSIT rule sets are XSD schemas plus Schematron compiled to XSLT 2.0 and run by Saxon (Java). This library reimplements the **full three-stage pipeline natively in PHP** — XSD schema, then the syntax rules (UBL-CR/SR, CII-SR/DT), then the business rules — with no JRE, no jar downloads and no subprocess, reading amounts as exact decimal strings (BCMath) so the tolerance-free calculation rules are exact.

Important

Every layer the **KoSIT validator configuration** enforces (validator-configuration-xrechnung 3.0.2 — the legal conformance target for XRechnung in Germany) is covered: the **XSD schemas**, all **1276 syntax rules**(UBL-CR/SR, CII-SR/DT) and **274 / 274 business rules** across EN 16931 core, the VAT categories, calculations, decimals, code lists and the German BR-DE / BR-DEX CIUS. Coverage is measured against that configuration and proven bidirectionally against the official KoSIT validator over its full test suite (see [COVERAGE.md](COVERAGE.md) and the conformance section below). This is a best-effort reimplementation, not certified software — do not treat a green result as a legal guarantee.

Requirements
------------

[](#requirements)

- PHP **8.4+**, `ext-bcmath`, `ext-dom`

Install
-------

[](#install)

```
composer require john-wink/en16931-php
```

Usage
-----

[](#usage)

```
use JohnWink\En16931\En16931Validator;

// validate() auto-detects CII vs UBL; validateCii()/validateUbl() force one.
$result = En16931Validator::xrechnung()->validate($xml); // or ::en16931()

$result->isValid();     // bool — no fatal violation
foreach ($result->violations as $violation) {
    echo "{$violation->ruleId} [{$violation->flag}]: {$violation->message}\n";
}
```

You can also validate a pre-built model (`En16931Validator::…->validateModel($invoice)`) without XML.

Rule coverage
-------------

[](#rule-coverage)

Validation pipeline
-------------------

[](#validation-pipeline)

Validation runs the three stages the official KoSIT validator applies — natively in PHP, no Java:

StageChecksRulesStatus1 — XSD schemaStructure of UBL 2.1 Invoice/CreditNote &amp; CII D16B—✅ bundled2 — Syntax rulesUBL-CR/SR &amp; CII-SR/DT code-list, cardinality &amp; datatype restrictions1276✅ 1276 / 12763 — Business rulesEN 16931 core + XRechnung CIUS/Extension274✅ 274 / 274Stage 2 breakdown: **UBL-CR 679, UBL-SR 50, CII-SR 450, CII-DT 97**. Evaluated directly against the DOM by `SchematronEngine` from `resources/syntax-rules.json` — proven against the KoSIT validator over the full official instance suite (0 false positives).

### Business rules

[](#business-rules)

**274 of 274 rules implemented (0 partial, 0 open)** — measured against the validator-configuration-xrechnung 3.0.2 (release 2025-03-21), the compiled Schematron the official KoSIT validator runs (and the parity oracle). The full per-rule matrix lives in [COVERAGE.md](COVERAGE.md).

Rule group✅🟡❌TotalBR — core document &amp; line rules600060BR-CO — conditions &amp; calculations250025BR-DEC — decimals210021BR-CL — code lists230023BR-AE — Reverse charge100010BR-IG — IGIC (Canary Islands)100010BR-IP — IPSI (Ceuta/Melilla)100010BR-IC — Intra-community supply120012BR-S — Standard rated100010BR-Z — Zero rated100010BR-E — Exempt from VAT100010BR-G — Export outside the EU100010BR-O — Not subject to VAT140014BR-B — Split payment (Italy)2002BR-DE — XRechnung CIUS320032BR-DEX — XRechnung Extension150015**Total****274**00**274**Full ISO 4217 and ISO 3166-1 code lists are bundled. Document-level allowances and charges (BG-20/BG-21) are modelled and reconciled.

The matrix is generated from the rule sets themselves — after adding a rule, run:

```
php tools/generate-coverage.php   # regenerates COVERAGE.md + this summary
```

`tests/Feature/CoverageDocTest.php` fails the suite when the docs drift from the code, and `tools/build-rules-reference.php` rebuilds the reference list from the KoSIT validator configuration (the same artefacts the parity test runs against), so the coverage denominator is exactly what the official validator enforces.

> The **Clean Vehicles Directive** profile (BR-DE-CVD-\*) is implemented too but lives under "Implemented beyond the reference" in [COVERAGE.md](COVERAGE.md): it is a separate KoSIT scenario, not part of the standard XRechnung configuration, and is enforced only for CVD-profile invoices.

Conformance / parity with KoSIT
-------------------------------

[](#conformance--parity-with-kosit)

The results are proven against the **official KoSIT validator** (the Java reference: validator 1.5.0 + validator-configuration-xrechnung 3.0.2), used as a dev/CI oracle — never shipped or required at runtime. `tests/Feature/ParityTest.php`runs the **entire official KoSIT test suite** (every UBL and CII instance across the standard, extension and technical-cases groups) through **both** this library and KoSIT — across all three layers (XSD, syntax rules and business rules) — and asserts:

1. **verdict parity** — accept/reject agrees on every instance, and
2. **no false positives** — every rule this library fires (BR-\*, UBL-CR/SR, CII-SR/DT), KoSIT fired too.

Reproduce it (needs a JRE):

```
bash tools/kosit-setup.sh          # downloads the KoSIT jar + config to build/kosit/
vendor/bin/pest tests/Feature/ParityTest.php
```

A second, Java-free check asserts this library fatally rejects **none** of the official positive instances — so the normal suite stays meaningful even without a JRE. The Java-backed parity tests skip automatically when Java or the KoSIT tools are absent; CI (`.github/workflows/ci.yml`) sets Java up and runs the full bidirectional comparison on every push.

> The oracle repeatedly paid for itself: it caught an incomplete BT-3 code list, several rule-id drifts against the shipped configuration (the Leitweg-ID is BR-DE-15, IGIC/IPSI are BR-IG/BR-IP), and rules present in the schematron source but not enforced by the configuration (BR-DE-TMP-32) — each of which would otherwise have rejected invoices the official validator accepts.

Quality gates
-------------

[](#quality-gates)

```
composer qa   # pint + rector + phpstan (max) + pest
```

License
-------

[](#license)

MIT. See [LICENSE.md](LICENSE.md).

###  Health Score

39

—

LowBetter than 84% of packages

Maintenance90

Actively maintained with recent releases

Popularity9

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity44

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~2 days

Total

3

Last Release

46d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/141d739e08a4293474c8598c867ff954a537c0bfd022e758ac183528c0f5cfb6?d=identicon)[john-wink](/maintainers/john-wink)

---

Top Contributors

[![john-wink](https://avatars.githubusercontent.com/u/35034627?v=4)](https://github.com/john-wink "john-wink (39 commits)")

---

Tags

validationZUGFeRDfactur-xxrechnungE-InvoiceciiEN16931germanye-rechnungschematron

###  Code Quality

TestsPest

Static AnalysisPHPStan, Rector

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

![Health badge](/badges/john-wink-en16931-php/health.svg)

```
[![Health](https://phpackages.com/badges/john-wink-en16931-php/health.svg)](https://phpackages.com/packages/john-wink-en16931-php)
```

###  Alternatives

[composer/semver

Version comparison library that offers utilities, version constraint parsing and validation.

3.3k547.4M1.2k](/packages/composer-semver)[giggsey/libphonenumber-for-php

A library for parsing, formatting, storing and validating international phone numbers, a PHP Port of Google's libphonenumber.

5.1k167.1M602](/packages/giggsey-libphonenumber-for-php)[respect/validation

The most awesome validation engine ever created for PHP

6.2k41.7M438](/packages/respect-validation)[propaganistas/laravel-phone

Adds phone number functionality to Laravel based on Google's libphonenumber API.

3.1k42.7M172](/packages/propaganistas-laravel-phone)[opis/json-schema

Json Schema Validator for PHP

65649.4M406](/packages/opis-json-schema)[horstoeko/zugferd

A library for creating and reading european electronic invoices

4357.1M40](/packages/horstoeko-zugferd)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
