[php-src] PR #25437: ext/dom: Add Dom\XMLSchema to reuse a compiled XML Schema.

From: Date: Fri, 13 Nov 2026 06:18:33 +0000
Subject: [php-src] PR #25437: ext/dom: Add Dom\XMLSchema to reuse a compiled XML Schema.
Groups: php.git-pulls 
Request: Send a blank email to git-pulls+get-40611@lists.php.net to get a copy of this message
Pull Request: https://github.com/php/php-src/pull/25437 Author: ArtUkrainskiy This is a proof of concept. I'd like feedback on the API before writing an RFC. schemaValidate() compiles the XSD on every call, validates, and frees the schema. For a real schema with imports, that compile is almost the whole cost. Apps that check many invoices or orders against one schema pay it on every document. This adds Dom\XMLSchema, which owns the compiled xmlSchemaPtr: ```php $schema = Dom\XMLSchema::createFromFile('/app/schemas/invoice.xsd'); // once per worker $schema->validate($doc); // Dom\XMLDocument|DOMDocument, fresh validation context per call ``` schemaValidate() now uses the same compile and validate helpers. Its behaviour doesn't change. The test schema is UBL 2.1 Invoice, an OASIS standard for e-invoices. Its main file imports shared type definitions, so compiling it reads 14 XSD files, 2.8 MB in total. The schema and the 1.2 KB and 20 KB invoices are the official ones from https://docs.oasis-open.org/ubl/os-UBL-2.1/UBL-2.1.zip. The larger invoices are the 20 KB example with its 5 item lines copied over and over until the invoice has 100, 1,000 or 10,000 lines. They still pass the schema. Time to load one document and validate it: | document | load + schemaValidate() | load + Dom\XMLSchema::validate() | |---|---:|---:| | UBL Invoice, 1.2 KB | 50.8 ms | 0.034 ms | | UBL Invoice, 20 KB | 51.0 ms | 0.31 ms | | UBL Invoice, 200 KB | 54.2 ms | 3.7 ms | | UBL Invoice, 1.9 MB | 88.9 ms | 37.2 ms | | UBL Invoice, 18.7 MB | 421 ms | 367 ms | Execution time, median of 9 alternating runs. Under callgrind one 20 KB validation is 433M instructions with schemaValidate() and 1.07M with validate(). schemaValidate() itself stays within noise after the refactor, at −0.06% instructions. Tests for ext/dom, ext/libxml and ext/xmlreader pass, and valgrind is clean on the new tests. A few things I'm not sure about. - On a broken schema it emits libxml warnings and then throws. Should it throw one exception that carries the errors instead? - createFromString() has no base URI, so relative imports resolve against the CWD, same as schemaValidateSource(). Should it take a ?string $baseUri? - Should it accept DOMDocument, or only Dom\XMLDocument? RelaxNG and XMLReader::setSchema() could follow the same pattern later. I haven't tried other compilers, other real-world schemas or an ASan build yet.

« previous php.git-pulls (#40611) next »