kalle / xml
Compact XML library for PHP with immutable and streaming writers, tree and streaming readers, reader queries, canonicalization, DOM interop, import, and XSD validation
Requires
- php: ^8.2
- ext-dom: *
- ext-libxml: *
- ext-xmlreader: *
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.80
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^11.5
README
kalle/xml is a compact, strict XML library for PHP 8.2+.
XmlBuilder builds immutable XML models. XmlWriter serializes complete
XmlDocument values. StreamingXmlWriter is the separate imperative writer
for incremental output to file and stream targets.
It provides:
XmlBuilderfor immutable, tree-based XML constructionXmlWriterfor serializing built documents to strings, files, or streamsStreamingXmlWriterfor incremental XML output to files and streamsStreamingXmlReaderfor incremental, cursor-based XML reading, subtree extraction, and non-overlapping record iteration viareadElements()XmlReaderfor read-only traversal of existing XMLXmlCanonicalizerfor deterministic canonical XML output across writer, reader, import, and DOM flowsXmlDomBridgeplus DOM entry points onXmlReaderfor explicit DOM interopfindAll()andfindFirst()for small namespace-aware element queries on the reader modelXmlImporterfor importing reader results back into the writer modelXmlValidatorfor validating XML against XSD schemas
The package stays intentionally narrow in scope. It covers XML writing, streaming XML reading, read-only tree loading, small reader-side queries, deterministic canonicalization, explicit DOM interop, reader-to-writer import, and XSD validation without trying to replace DOM, wrap all of XPath, or become a broad XML framework.
Installation
composer require kalle/xml
Runtime requirements: ext-dom, ext-libxml, and ext-xmlreader.
Current Scope
Included:
- tree-based XML building with
XmlBuilder - document serialization with
XmlWriter::toString(),toFile(), andtoStream() - streaming XML writing with
StreamingXmlWriter - streaming XML reading and non-overlapping record iteration with
StreamingXmlReader - read-only XML loading with
XmlReader - canonical XML output with
XmlCanonicalizer - explicit DOM interop with
XmlDomBridgeandXmlReader::fromDomDocument()/fromDomElement() - small namespace-aware element queries with
findAll()andfindFirst() - reader-to-writer import with
XmlImporter - compact XSD validation with
XmlValidator
Out of scope:
- mutation APIs for loaded XML
- broad DOM or XPath wrapper APIs
- XML-to-array or XML-to-object mapping
- XML diff, patch, merge, or signature tooling
- broad schema-framework features beyond compact XSD validation
Quick Example
<?php declare(strict_types=1); use Kalle\Xml\Builder\XmlBuilder; use Kalle\Xml\Writer\XmlWriter; $document = XmlBuilder::document( XmlBuilder::element('catalog') ->child( XmlBuilder::element('book') ->attribute('isbn', '9780132350884') ->child(XmlBuilder::element('title')->text('Clean Code')), ), ); echo XmlWriter::toString($document);
When To Use Each API
- Use
XmlBuilderwhen you want to build an immutable XML tree in memory, reuse subtrees, or keep fixtures readable. - Use
XmlWriterwhen you already have a builtXmlDocumentand want a string, file, or stream. - Use
StreamingXmlWriterwhen output is incremental, large, or should go straight to a file or stream. - Use
StreamingXmlReaderwhen input is large or incremental and you only need cursor-style inspection, non-overlapping record-by-record processing throughreadElements(), subtree extraction, or filtered export. - Use
XmlReaderwhen you want a loaded tree for traversal, parent/child navigation, or queries. - Use
XmlCanonicalizerwhen stable canonical XML output matters for snapshots, comparison, hashing, or deduplication. - Use DOM interop when writer-side or reader-side flows need to connect to existing
DOMDocumentorDOMElementvalues without adopting a mutable DOM wrapper. - Use reader queries when
findAll()orfindFirst()is clearer than repeated traversal. - Use
XmlImporterwhen reader results need to move back into the writer-side model. - Use
XmlValidatorwhen XML must match an XSD schema.
That split is intentional: building, whole-document serialization, and incremental streaming stay separate so the package stays compact.
Documentation
- Documentation index
- Getting Started
- Writer guides
- Reader guides
- Streaming reader guide
- Canonicalization guide
- DOM interop guide
- Import guides
- Validation guides
- Choosing an API
- Work with Namespaces
- API reference
- Changelog
- Examples
- Streaming reader examples: streaming-reader-catalog.php, streaming-reader-invoice.php, streaming-reader-feed-export.php
- Canonicalization example: canonicalize-feed.php
- DOM examples: dom-roundtrip.php, dom-feed-query.php, dom-invoice-stream.php