PHPackages                             xterr/php-espd - 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. [Parsing &amp; Serialization](/categories/parsing)
4. /
5. xterr/php-espd

ActiveLibrary[Parsing &amp; Serialization](/categories/parsing)

xterr/php-espd
==============

Typed PHP classes for ESPD (European Single Procurement Document) based on ESPD-EDM v4.1.0 / UBL 2.3

1.4.0(3mo ago)021MITXSLTPHP ^8.2CI passing

Since Apr 10Pushed 3mo agoCompare

[ Source](https://github.com/xterr/php-espd)[ Packagist](https://packagist.org/packages/xterr/php-espd)[ RSS](/packages/xterr-php-espd/feed)WikiDiscussions main Synced 3w ago

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

PHP ESPD
========

[](#php-espd)

[![PHP](https://camo.githubusercontent.com/ccaa43fc634d348cffccb1d8db7b55d9f17c5d46944bc99a15c3c982724b387d/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048502d382e322532422d3737374242343f6c6f676f3d706870266c6f676f436f6c6f723d7768697465)](https://www.php.net/)[![License: MIT](https://camo.githubusercontent.com/fdf2982b9f5d7489dcf44570e714e3a15fce6253e0cc6b5aa61a075aac2ff71b/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d79656c6c6f772e737667)](https://opensource.org/licenses/MIT)[![Packagist Version](https://camo.githubusercontent.com/96827bb9008dd7646114dbf12823049bd1b732dcda39c3160d14acdbcc4d82b6/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f78746572722f7068702d65737064)](https://packagist.org/packages/xterr/php-espd)[![PHPStan Level 8](https://camo.githubusercontent.com/ff3c7f8c8667ce643f47e74532748f673482a5f95d7d4269f925f2eebbe5117e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048505374616e2d6c6576656c253230382d627269676874677265656e)](https://phpstan.org/)

Typed PHP classes for ESPD (European Single Procurement Document) based on ESPD-EDM v2.1.1–v4.1.0 / UBL 2.3.

Serialize and deserialize `QualificationApplicationRequest` and `QualificationApplicationResponse` documents without DOM code, with multi-version support (v2.1.1 through v4.1.0). Validate documents against the official OP-TED Schematron business rules with 1:1 compliance. Codelist values are PHP enums with full type safety, including union-typed properties for elements that span multiple codelists.

Features
--------

[](#features)

- **325 Generated Classes** — 2 document roots, 290 aggregate types, 8 leaf value types, 16 codelist enums, 5 XSD enums, 2 registries
- **Business Rule Validation** — Official OP-TED ESPD-EDM Schematron rules for v2.1.1, v3.3.0, v4.0.0, and v4.1.0 via SaxonC-HE XSLT processor, 1:1 compliant with the EU reference validator
- **Codelist Enums** — `CriterionCode`, `CriterionElement`, `ResponseData`, `PropertyGroup`, `CountryCode`, `LanguageCode`, and 10 more
- **Union Enum Types** — `ExpectedCode` resolves to `BooleanGUIControl|FinancialRatio|OccupationCode` based on `listID` at runtime
- **Multi-Version Support** — Deserialize and validate ESPD documents from v2.1.1, v3.3.0, v4.0.0, and v4.1.0. V2 long-form criterion codes automatically merge into the `CriterionCode` enum with `V2_` prefix and bidirectional v2↔v4 mapping
- **ESPD Part &amp; Section Classification** — `CriterionCode::getPart()` returns the `EspdPart` enum (I–VI per [EU Regulation 2016/7](https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:32016R0007)), `getSection()` returns the sub-section letter (A–D, α) per [Directive 2014/24/EU](https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:32014L0024) Articles 57–62
- **Criterion Taxonomy** — `ESPD-criterion.xml` ships as a resource, deserializable into the generated classes
- **Full Round-Trip** — serialize → deserialize produces identical object graphs

Installation
------------

[](#installation)

```
composer require xterr/php-espd
```

**Requirements:**

- PHP 8.2 or higher
- `ext-dom`
- `ext-libxml`

**Optional (for validation):**

- `ext-saxonc` — [SaxonC-HE 12.x](https://www.saxonica.com/download/c.html) (free, MPL-2.0 license)

Quick Start
-----------

[](#quick-start)

### Deserialize an ESPD Request

[](#deserialize-an-espd-request)

```
use Xterr\UBL\Xml\XmlDeserializer;
use Xterr\Espd\Doc\QualificationApplicationRequest;

$xml = file_get_contents('espd-request.xml');

$deserializer = new XmlDeserializer();
$request = $deserializer->deserialize($xml, QualificationApplicationRequest::class);

echo $request->getId()->getValue();                    // "ESPDREQ-..."
echo $request->getProfileExecutionID()->getValue();    // "ESPD-EDMv4.1.0"
echo count($request->getTenderingCriterions());        // 62
```

### Serialize to XML

[](#serialize-to-xml)

```
use Xterr\UBL\Xml\XmlSerializer;

$serializer = new XmlSerializer();
$xml = $serializer->serialize($request);
```

### Validate against EU business rules

[](#validate-against-eu-business-rules)

Requires the `ext-saxonc` PHP extension ([installation guide](#installing-saxonc-he)).

```
use Xterr\Espd\Validation\EspdValidator;

$validator = EspdValidator::create();
$result = $validator->validate($request);

if (!$result->isValid()) {
    foreach ($result->getFailures() as $violation) {
        echo $violation . PHP_EOL;
        // [fatal] BR-OTH-04-01: The element '/cbc:UBLVersionID' is mandatory. (at /*)
    }
}

// Filter by severity
$result->getFatals();    // Severity::Fatal only
$result->getErrors();    // Severity::Error only
$result->getWarnings();  // Severity::Warning only
$result->getFailures();  // Fatal + Error combined
```

You can also validate raw XML strings directly:

```
use Xterr\Espd\Validation\DocumentType;

$xml = file_get_contents('espd-response.xml');
$result = $validator->validateXml($xml, DocumentType::Response);
```

### Work with typed enums

[](#work-with-typed-enums)

```
use Xterr\Espd\Codelist\CriterionCode;
use Xterr\Espd\Codelist\CriterionElement;
use Xterr\Espd\Codelist\ResponseData;

$criterion = $request->getTenderingCriterions()[0];

// CriterionCode enum — not a raw string
$typeCode = $criterion->getCriterionTypeCode();        // CriterionCode::CRIME_ORG
echo $typeCode->value;                                  // "crime-org"

$group = $criterion->getTenderingCriterionPropertyGroups()[0];
$prop = $group->getTenderingCriterionProperties()[0];

$prop->getTypeCode();                                   // CriterionElement::QUESTION
$prop->getValueDataTypeCode();                          // ResponseData::INDICATOR
```

### Multi-version deserialization

[](#multi-version-deserialization)

The library supports ESPD documents from all major versions. V2 long-form criterion codes are automatically resolved:

```
// V2 document — long-form codes resolve to V2_ prefixed enum cases
$v2Request = $deserializer->deserialize($v2Xml, QualificationApplicationRequest::class);
$code = $v2Request->getTenderingCriterions()[0]->getCriterionTypeCode();
// CriterionCode::V2_CRITERION_EXCLUSION_CONVICTIONS_CORRUPTION

$code->isLegacy();        // true — it's a v2 long-form code
$code->toV4Equivalent();  // CriterionCode::CORRUPTION — the v4 equivalent

// V3/V4 documents — short-form codes work as before
$v4Request = $deserializer->deserialize($v4Xml, QualificationApplicationRequest::class);
$code = $v4Request->getTenderingCriterions()[0]->getCriterionTypeCode();
// CriterionCode::CRIME_ORG

$code->isLegacy();        // false
$code->toV4Equivalent();  // CriterionCode::CRIME_ORG (returns self)
```

### Criterion Part &amp; Section classification

[](#criterion-part--section-classification)

Every criterion code knows its position in the ESPD form hierarchy defined by [Commission Implementing Regulation (EU) 2016/7](https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:32016R0007):

```
use Xterr\Espd\Codelist\CriterionCode;
use Xterr\Espd\Codelist\EspdPart;

$code = CriterionCode::CORRUPTION;

$code->getPart();      // EspdPart::III
$code->getPart()->label(); // "Exclusion grounds"
$code->getSection();   // "A" — Criminal convictions (Directive 2014/24/EU Art. 57(1))

// Works for all codes — v4 short-form and v2 legacy
CriterionCode::PROF_REGIST->getPart();    // EspdPart::IV (Selection criteria)
CriterionCode::PROF_REGIST->getSection(); // "A" (Suitability)

CriterionCode::SME->getPart();            // EspdPart::II (Economic operator info)
CriterionCode::SME->getSection();         // "A"

CriterionCode::STAFF_RED->getPart();      // EspdPart::V (Reduction of candidates)

// V2 codes inherit Part/Section from their v4 equivalent
CriterionCode::V2_CRITERION_EXCLUSION_CONVICTIONS_CORRUPTION->getPart();    // EspdPart::III
CriterionCode::V2_CRITERION_EXCLUSION_CONVICTIONS_CORRUPTION->getSection(); // "A"
```

**ESPD Form Parts** (per EU Regulation 2016/7, Annex 2):

PartTitleSectionsIInformation concerning the procurement procedure—IIInformation concerning the economic operatorA, B, C, DIIIExclusion groundsA (Criminal convictions), B (Taxes/social security), C (Insolvency/misconduct), D (National grounds)IVSelection criteriaα (Global indication), A (Suitability), B (Economic standing), C (Technical ability), D (Quality/environment certs)VReduction of the number of qualified candidates—VIConcluding statements—### Union enum — ExpectedCode

[](#union-enum--expectedcode)

`ExpectedCode` can hold values from three different codelists depending on the criterion context. The `listID` XML attribute acts as the runtime discriminator:

```
// The property type is: BooleanGUIControl|FinancialRatio|OccupationCode|null
$expectedCode = $prop->getExpectedCode();

if ($expectedCode instanceof \Xterr\Espd\Codelist\BooleanGUIControl) {
    // e.g. BooleanGUIControl::CHECKBOX_TRUE
}
```

### Load the criterion taxonomy

[](#load-the-criterion-taxonomy)

The full ESPD criterion tree ships as a resource:

```
$taxonomyXml = file_get_contents('vendor/xterr/php-espd/resources/criterion/v4.1.0/ESPD-criterion.xml');
$taxonomy = $deserializer->deserialize($taxonomyXml, QualificationApplicationRequest::class);

// 62 criteria with their property groups, questions, and response types
foreach ($taxonomy->getTenderingCriterions() as $criterion) {
    echo $criterion->getCriterionTypeCode()->value . "\n";
}
```

Validation
----------

[](#validation)

The validation subsystem runs the official [OP-TED/ESPD-EDM](https://github.com/OP-TED/ESPD-EDM) Schematron business rules via SaxonC-HE. This guarantees 1:1 compliance with the EU reference validator — identical rule IDs, severity levels, and error messages.

### Multi-version validation

[](#multi-version-validation)

The validator supports **4 ESPD-EDM version families** and automatically detects which rule set to apply based on the document's `ProfileExecutionID`:

Version FamilyRule SetProfileExecutionID values`VersionFamily::V2`v2.1.1`ESPD-EDMv2.0.0-REGULATED`, `ESPD-EDMv2.0.0-SELFCONTAINED`, `ESPD-EDMv2.1.0-REGULATED`, `ESPD-EDMv2.1.0-SELFCONTAINED`, `ESPD-EDMv2.1.1-BASIC`, `ESPD-EDMv2.1.1-EXTENDED``VersionFamily::V3`v3.3.0`ESPD-EDMv3.0.0`, `ESPD-EDMv3.0.1`, `ESPD-EDMv3.1.0`, `ESPD-EDMv3.2.0`, `ESPD-EDMv3.3.0``VersionFamily::V4_0`v4.0.0`ESPD-EDMv4.0.0``VersionFamily::V4_1`v4.1.0`ESPD-EDMv4.1.0`**Auto-detection** (recommended) — the validator reads `ProfileExecutionID` from the XML and selects the matching rule set:

```
use Xterr\Espd\Validation\EspdValidator;

$validator = EspdValidator::create();

// Version is auto-detected from the document's ProfileExecutionID
$result = $validator->validate($request);
$result = $validator->validateXml($xml, DocumentType::Request);
```

**Explicit version** — useful when the document lacks a `ProfileExecutionID` or you want to force a specific rule set:

```
use Xterr\Espd\Validation\VersionFamily;

$result = $validator->validate($request, VersionFamily::V2);
$result = $validator->validateXml($xml, DocumentType::Response, VersionFamily::V3);
```

If auto-detection fails (missing or unrecognized `ProfileExecutionID`), a `ValidationException` is thrown prompting you to specify the version explicitly.

### What gets validated

[](#what-gets-validated)

CategoryRule IDsSeverityCardinality`BR-OTH-04-*`FatalCodelist valuesauto-generatedFatalCriterion structure`BR-TC-02` through `BR-TC-21`Fatal/ErrorExclusion criteria`BR-REQ-30`FatalSelection criteria`BR-REQ-40`WarningProcurer data`BR-REQ-20-*`Warning/ErrorEconomic operator`BR-RESP-10-*`, `BR-RESP-20-*`ErrorOther`BR-SC-10`, `BR-OTH-*`Mixed### Validation result API

[](#validation-result-api)

```
$result->isValid();      // true if no Fatal or Error violations
$result->hasWarnings();  // true if any Warning violations
$result->getFatals();    // list — Fatal only
$result->getErrors();    // list — Error only
$result->getFailures();  // list — Fatal + Error
$result->getWarnings();  // list — Warning only
$result->violations;     // list — all violations
count($result);          // total violation count
```

Each `Violation` exposes:

PropertyDescription`ruleId`Schematron rule ID (e.g. `BR-OTH-04-01`)`severity``Severity::Fatal`, `Severity::Error`, or `Severity::Warning``message`Human-readable error message`location`XPath to the failing XML node`test`XPath test expression from the rule`pattern`Pattern group IDGenerated Structure
-------------------

[](#generated-structure)

```
src/
├── Cbc/           8 leaf value types (Amount, Code, Identifier, Text, ...)
├── Cac/         290 aggregate types (TenderingCriterion, Party, Address, ...)
├── Doc/           2 document roots
│   ├── QualificationApplicationRequest.php
│   └── QualificationApplicationResponse.php
├── Codelist/     16 codelist enums from Genericode files
├── Enum/          5 XSD-defined enums
├── Validation/    8 hand-written validation classes
│   ├── EspdValidator.php
│   ├── ValidationResult.php
│   ├── Violation.php
│   ├── Severity.php
│   ├── DocumentType.php
│   ├── SvrlParser.php
│   └── Exception/
└── Xml/           2 registries (DocumentRegistry, TypeMap)

```

Codelist Enums
--------------

[](#codelist-enums)

EnumlistIDValues`CriterionCode``criterion``crime-org`, `corruption`, `fraud`, ... (+ `getPart()`, `getSection()`)`EspdPart`—`I`, `II`, `III`, `IV`, `V`, `VI` (EU Regulation 2016/7 form Parts)`CriterionElement``criterion-element-type``QUESTION`, `REQUIREMENT`, `CRITERION`, ...`ResponseData``response-data-type``INDICATOR`, `AMOUNT`, `DATE`, `DESCRIPTION`, ...`PropertyGroup``property-group-type``ON*`, `ONTRUE`, `ONFALSE``CountryCode``country`ISO 3166-1 codes`LanguageCode``language`ISO 639 codes`CurrencyCode``currency`ISO 4217 codes`OccupationCode``occupation`ESCO occupation codes`EconomicOperatorSize``economic-operator-size`SME, micro, large, ...`EoRole``eo-role-type`Sole tenderer, lead entity, ...`BooleanGUIControl``boolean-gui-control-type``CHECKBOX_TRUE`, `RADIO_BUTTON_TRUE`, ...`FinancialRatio``financial-ratio-type`Ratio types`ProfileExecutionID``profile-execution-id``ESPD-EDMv4.1.0`, ...`AccessRight``access-right`Access right codes`Docrefcontent``docrefcontent-type`Document reference content types`EOID``eoid-type`Economic operator ID typesCodelist Bindings
-----------------

[](#codelist-bindings)

These properties are typed with codelist enums instead of generic `Code`:

ClassPropertyEnum`TenderingCriterion``criterionTypeCode``CriterionCode``TenderingCriterionProperty``typeCode``CriterionElement``TenderingCriterionProperty``valueDataTypeCode``ResponseData``TenderingCriterionProperty``expectedCode``BooleanGUIControl|FinancialRatio|OccupationCode``TenderingCriterionPropertyGroup``propertyGroupTypeCode``PropertyGroup``Country``identificationCode``CountryCode``Language``localeCode``LanguageCode`Installing SaxonC-HE
--------------------

[](#installing-saxonc-he)

The `ext-saxonc` PHP extension is required only for validation. Serialization and deserialization work without it.

### Docker (easiest)

[](#docker-easiest)

```
FROM php:8.2-fpm

COPY --from=ghcr.io/mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/local/bin/

RUN IPE_SAXON_EDITION=HE install-php-extensions saxon
```

### macOS

[](#macos)

```
curl -LO https://downloads.saxonica.com/SaxonC/HE/12/SaxonCHE-macos-arm64-12-9-0.zip
unzip SaxonCHE-macos-arm64-12-9-0.zip
xattr -dr com.apple.quarantine SaxonCHE-macos-arm64-12-9-0

sudo cp -P SaxonCHE-macos-arm64-12-9-0/SaxonCHE/lib/libsaxonc-*.dylib /usr/local/lib/

cd SaxonCHE-macos-arm64-12-9-0/php/src
phpize && ./configure --with-saxon=../../SaxonCHE && make -j$(nproc) && sudo make install
sudo install_name_tool -add_rpath /usr/local/lib $(php -r "echo ini_get('extension_dir');")/saxon.so

echo "extension=saxon.so" > $(php --ini | grep "Scan for" | awk -F: '{print $2}' | xargs)/50-saxon.ini
```

For Intel Macs, replace `arm64` with `x86_64` in the download URL.

### Linux (Ubuntu/Debian)

[](#linux-ubuntudebian)

```
sudo apt-get install -y php8.2-dev build-essential unzip wget libstdc++6

wget https://downloads.saxonica.com/SaxonC/HE/12/SaxonCHE-linux-x86_64-12-9-0.zip
unzip SaxonCHE-linux-x86_64-12-9-0.zip

sudo cp SaxonCHE-linux-x86_64-12-9-0/SaxonCHE/lib/libsaxon*.so /usr/local/lib/ && sudo ldconfig

cd SaxonCHE-linux-x86_64-12-9-0/php/src
phpize && ./configure --with-saxon=../../SaxonCHE && make -j$(nproc) && sudo make install
echo "extension=saxon.so" | sudo tee /etc/php/8.2/mods-available/saxon.ini && sudo phpenmod saxon
```

### Verify

[](#verify)

```
php -r "echo (new Saxon\SaxonProcessor())->version() . PHP_EOL;"
# SaxonC-HE 12.9 from Saxonica
```

Schema Source
-------------

[](#schema-source)

Generated from [OP-TED/ESPD-EDM](https://github.com/OP-TED/ESPD-EDM) v4.1.0, which uses OASIS UBL 2.3 OS schemas. The XSD schemas and Genericode codelist files are in `resources/` (not committed — dev-only). The pre-compiled Schematron XSL validation rules are bundled in `resources/validation/` for all 4 version families (v2.1.1, v3.3.0, v4.0.0, v4.1.0). V2.1.1 codelist GC files are in `resources/cl/gc/`.

Regenerating
------------

[](#regenerating)

The `php-espd` CLI tool orchestrates the full code generation pipeline:

```
php php-espd espd:generate --force
```

### What it does

[](#what-it-does)

1. **Parses criterion taxonomies** — reads ESPD-EDM v2.1.1 and v4.1.0 taxonomy XML files
2. **Cross-references UUIDs** — matches v2 long-form codes to v4 short-form codes by criterion UUID (+ 6 manual EO\_DATA equivalences for codes where v4 uses structured IDs instead of UUIDs)
3. **Generates v2→v4 mapping file** — writes `resources/criterion/v2-to-v4-mapping.php` (v2 → v4 lookup) 3b. **Generates code-to-part mapping** — parses `criterionList.xml` for exclusion/selection classification, resolves sub-sections from v2 code prefixes and [EU Regulation 2016/7](https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:32016R0007) Annex 2, writes `resources/criterion/code-to-part.php`
4. **Generates Genericode file** — writes `resources/codelists/gc/CriteriaTypeCode.gc` with all v2 codes
5. **Runs UBL generator** — regenerates all PHP classes, codelist enums, and registries from XSD schemas and Genericode files

The generator automatically merges v2 criterion codes into `CriterionCode` with a `V2_` prefix and injects the `CriterionCodeMethods` trait for v2↔v4 conversion.

### Options

[](#options)

OptionDescription`--force`, `-f`Actually generate files (without this, dry-run only)`--codelists-only`Only regenerate codelist enums (skip class generation)`--mapping-only`Only regenerate v2→v4 mapping files (skip UBL generator)### Example output

[](#example-output)

```
ESPD PHP Code Generator
=======================

Step 1: Parsing criterion taxonomies
  V2 taxonomy: 66 criteria
  V4 taxonomy: 62 criteria

Step 2: Cross-referencing UUIDs
  Mapped: 61 codes, V2-only: 5 codes

Step 3: Generating v2→v4 mapping file
  → resources/criterion/v2-to-v4-mapping.php

Step 3b: Generating code-to-part mapping
  Criterion list: 55 codes classified
  Part mapping: 130 codes total
  → resources/criterion/code-to-part.php

Step 4: Generating CriteriaTypeCode.gc
  → resources/codelists/gc/CriteriaTypeCode.gc

Step 5: Running UBL generator
  Loading XSD schemas...
  Emitting codelist enums...

Result
  UBL 2.3: 8 CBC classes, 292 CAC classes, 2 document roots, 5 enums, 16 codelist enums, 325 total files

```

### Multi-version support

[](#multi-version-support)

The generated `CriterionCode` enum contains both v4 short codes (`crime-org`, `corruption`, ...) and v2 long-form codes prefixed with `V2_` (`CRITERION.EXCLUSION.CONVICTIONS.PARTICIPATION_IN_CRIMINAL_ORGANISATION`, ...). This allows deserializing ESPD documents from both v2.x and v4.x without data loss:

```
$code = CriterionCode::V2_CRITERION_EXCLUSION_CONVICTIONS_CORRUPTION;

$code->isLegacy();        // true
$code->toV4Equivalent();  // CriterionCode::CORRUPTION
```

Development
-----------

[](#development)

```
# Install dependencies
composer install

# Run tests
composer test

# Run static analysis
composer analyze

# Regenerate all classes and codelists
php php-espd espd:generate --force

# Regenerate only codelist enums (faster)
php php-espd espd:generate --force --codelists-only
```

EU Legislation References
-------------------------

[](#eu-legislation-references)

The ESPD form structure and criterion classification are defined by EU legislation:

DocumentReferenceRelevance[Commission Implementing Regulation (EU) 2016/7](https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:32016R0007)Standard form for the ESPDDefines the 6-part form structure (Parts I–VI) with sections A–D, used by `EspdPart` enum and `getPart()`/`getSection()`[Directive 2014/24/EU](https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:32014L0024)Public procurement directiveArt. 57: exclusion grounds (Parts III-A/B/C/D), Art. 58: selection criteria (Parts IV-A/B/C), Art. 62: quality/environment certificates (Part IV-D)[ESPD-EDM](https://github.com/OP-TED/ESPD-EDM) (OP-TED)Exchange Data ModelSource of criterion taxonomy XML, Schematron validation rules, and Genericode codelists used by this libraryLicense
-------

[](#license)

[MIT](LICENSE) - Copyright (c) 2026 Ceana Razvan

###  Health Score

38

—

LowBetter than 83% of packages

Maintenance81

Actively maintained with recent releases

Popularity6

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity50

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 ~0 days

Total

5

Last Release

102d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/d9526b453abaa7d569fdacdf58cb0f55aa8d889f68f5b622344a28b37eb6c17d?d=identicon)[razvanceana](/maintainers/razvanceana)

---

Top Contributors

[![xterr](https://avatars.githubusercontent.com/u/619509?v=4)](https://github.com/xterr "xterr (9 commits)")

---

Tags

xmlxsdublqualificationprocurementespde-procurement

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/xterr-php-espd/health.svg)

```
[![Health](https://phpackages.com/badges/xterr-php-espd/health.svg)](https://phpackages.com/packages/xterr-php-espd)
```

###  Alternatives

[veewee/xml

XML without worries

1837.0M39](/packages/veewee-xml)[goetas-webservices/xsd2php

Convert XSD (XML Schema) definitions into PHP classes and JMS metadata

2391.7M45](/packages/goetas-webservices-xsd2php)[goetas-webservices/xsd2php-runtime

Convert XSD (XML Schema) definitions into PHP classes

4912.6M45](/packages/goetas-webservices-xsd2php-runtime)[goetas-webservices/xsd-reader

Read any XML Schema (XSD) programmatically with PHP

625.2M22](/packages/goetas-webservices-xsd-reader)[goetas-webservices/wsdl-reader

Pure PHP WSDL parser

10351.5k7](/packages/goetas-webservices-wsdl-reader)[easybill/e-invoicing

A package to read and create EN16931 e-invoices or CIUS like: XRechnung, ZUGFeRD etc.

14158.3k](/packages/easybill-e-invoicing)

PHPackages © 2026

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