greenter/ubl-validator
Validate UBL (OASIS Universal Business Language) XML documents against official XSD schemas. Automatically resolves the UBL version, locates the matching schema, and reports validation errors. Supports custom XSD directories for other UBL versions.
Architecture Fit
greenter/ubl-validator is a single-responsibility component: it reads <cbc:UBLVersionID>, resolves the matching XSD from a base directory, runs schemaValidate(), and returns a boolean plus an error string. That is the entire contract. For a TPM this is a feature, not a limitation — it slots in as an anti-corruption layer at the ingestion boundary (API payload, file upload, queue job, migration batch) with no framework coupling and no transitive dependency surface beyond PHP and ext-libxml/ext-dom.
Strategically, position it as one gate in a validation pipeline, not the compliance layer. XSD validation answers "is this structurally a UBL document of version X." It does not answer "will SUNAT/tax authority accept it" — business rules, catalogs, tax calculations, and local extensions live elsewhere (typically greenter/lite and SUNAT's rule set). Scope creep here is the primary architectural risk: stakeholders will hear "validator" and assume it means "compliant."
Integration Feasibility
Composer-installable, MIT, two lines to first result, one configurable property (pathResolver->baseDirectory) for custom schemas. A spike is measured in hours, not sprints. The only non-obvious work is the plumbing you must own: a service provider binding, a wrapper that turns raw libxml error strings into structured, localized, API-safe errors, and a queued path for bulk/large documents. None of that is provided.
Technical Risk
| Risk | Severity | Assessment |
|---|---|---|
| Schema/version drift | High | UBL and jurisdiction-specific schemas move on a regulatory calendar, not a library calendar. Release v2.3.0 is explicitly tied to a SUNAT validation change (2026-08-26), which proves the maintainer tracks it — and also means you inherit an external compliance deadline. If upstream lags, you must vendor XSDs yourself. |
| Bus factor / ecosystem signal | Medium-High | 16 stars, 0 dependents, effectively one maintainer. Weak third-party validation of the package. Mitigant: it sits inside the Greenter ecosystem, which is the de facto Peruvian e-invoicing stack — the signal is ecosystem-internal, not GitHub-popularity-driven. Judge it on that, not on stars. |
| Silent scope gap | High | Teams will over-trust a true result. XSD pass ≠ fiscal acceptance. Must be documented in the integration contract, not just the README. |
| libxml behavior variance | Medium | Schema validation output and error text can vary across libxml/PHP builds. Pin the runtime image; don't let dev/staging/prod diverge on PHP minor versions. |
| Single-maintainer responsiveness | Medium | Precedent exists (external PR merged in v2.3.0), so contribution is viable, but there is no SLA. Budget for the possibility that you patch and vendor. |
Key Questions
Stack Fit
Zero Laravel coupling is the selling point. Bind UblValidator in a service provider behind your own interface (UblValidatorContract), not the concrete class. That one indirection buys you the ability to swap in a hosted validator, a forked version with custom XSDs, or a stub in tests without touching call sites. Configure baseDirectory from config/ pointing at a versioned, app-owned XSD directory rather than relying on the package's bundled set — the bundled set is intentionally partial.
Migration Path
DOMDocument::schemaValidate code: straightforward; wrap the existing call site, run both in shadow mode against a fixture corpus of real accepted/rejected documents, diff results, then cut over. Expect near-zero divergence and quick confidence.Compatibility
Verify PHP constraint against your fleet, pin the container image's libxml, and confirm the custom XSD directory convention ({version}/common/, {version}/maindoc/) matches whatever you vendor. Treat "which UBL versions ship in src/xsd" as a question to answer per release, not an assumption — the README is explicit that not all schemas are bundled.
Sequencing
Maintenance
The real ongoing cost is XSD lifecycle management, not code. Budget a recurring (quarterly, plus event-driven on regulatory announcements) task: watch upstream releases, diff the bundled XSDs, bump the pinned version, re-run the golden fixture suite, deploy. Vendor XSDs into your own repo or artifact store so a package update is a reviewable diff rather than an opaque composer update. Keep the dependency on a tagged release, never dev-master — this package ships compliance-relevant schema changes.
Support There is no vendor and no meaningful community tier. Your platform team is tier 1 and tier 2. Upstream engagement is realistic but not guaranteed on any timeline; maintain a small internal fork capability (patch XSD + tag) as a contingency, and only exercise it when a regulatory deadline demands it. Document this fallback so it isn't invented during an incident.
Scaling Validation is CPU-bound and per-document cheap — typical invoices validate in milliseconds; the cost is dominated by XSD parsing per call, so per-process batching amortizes poorly unless you reuse the validator instance where safe. Keep it off the synchronous request path for anything bulk or above a few MB; route through queue workers with bounded concurrency and a document-size cap. Horizontal scale is trivial because the component is stateless — this is a good sign for capacity planning.
Failure Modes
false. Wrap in try/catch and always pair with libxml_use_internal_errors(true) / libxml_clear_errors(); global libxml state leaking between requests is a real hazard in long-running workers.Ramp-up Engineer ramp-up on the package itself: under a day for a mid-level PHP developer. The genuine ramp is domain literacy — UBL structure, namespaces, version differences, and which validation layer owns which class of error. Plan for one person to become the internal owner of both the package and the XSD lifecycle; that role, not the code, is what keeps this reliable.
How can I help you explore Laravel packages today?