This document provides a complete set of migration rules for upgrading from veewee/xml v3.x to v4.x.
The primary driver behind v4 is PHP 8.4's new spec-compliant DOM API (Dom\ namespace), which replaces the legacy DOM extension classes.
- PHP 8.4+ is required (v3 supported PHP 8.2/8.3)
- The new
Dom\namespace classes are part of PHP 8.4's opt-in DOM spec compliance (RFC)
| veewee/xml | PHP | LTS |
|---|---|---|
| 3.0 - 3.1 | 8.1, 8.2, 8.3 | NO |
| 3.2 | 8.2, 8.3 | NO |
| 3.3+ | 8.2, 8.3, 8.4 | YES |
| 4.0+ | 8.4+ | YES |
Step 1: Upgrade to v3.3+ and PHP 8.4 first (v3.3+ supports PHP 8.4 as a bridge). Step 2: Apply the migration rules below and bump to v4.0.
- "php": "~8.2.0 || ~8.3.0",
+ "php": "~8.4.0",The entire DOM layer moved from legacy DOM* classes to the new Dom\ namespace.
PHP 8.4 introduces Dom\Document as a base class (extended by Dom\XMLDocument and Dom\HTMLDocument).
The veewee wrapper is VeeWee\Xml\Dom\Document. These are different classes in different namespaces.
Most consuming code only needs VeeWee\Xml\Dom\Document as the entry point and does not need to import native Dom\* classes directly. The native types only surface when:
- You define custom configurators, builders, validators, or loaders (their callable signatures changed, e.g.
callable(DOMDocument): DOMDocumentbecamecallable(Dom\XMLDocument): Dom\XMLDocument) - You call
toUnsafeDocument()and pass the result around (now returnsDom\XMLDocument) - You type-hint DOM nodes in your own code (e.g.
DOMElementin function parameters)
If your code exclusively uses the veewee Document wrapper and its provided functions, the migration is mostly updating veewee/xml in composer.json and fixing any custom callables.
When you do need to update native DOM type references, apply these replacements. Order matters: replace longer/more specific strings first to avoid partial matches.
| v3 (Search) | v4 (Replace) |
|---|---|
DOMCdataSection |
Dom\CDATASection |
DOMDocument |
Dom\XMLDocument |
DOMNodeList |
Dom\NodeList |
DOMElement |
Dom\Element |
DOMXPath |
Dom\XPath |
DOMNode |
Dom\Node |
DOMAttr |
Dom\Attr |
Note:
DOMDocumentdoes NOT becomeDom\Document. It becomesDom\XMLDocumentspecifically.Dom\Documentis the abstract base class in PHP 8.4; you almost never reference it directly. Note:DOMCdataSectionbecomesDom\CDATASection(capitalization differs).
- use DOMDocument;
- use DOMElement;
- use DOMNode;
- use DOMXPath;
- use DOMNodeList;
- use DOMAttr;
- use DOMCdataSection;
+ use Dom\XMLDocument;
+ use Dom\Element;
+ use Dom\Node;
+ use Dom\XPath;
+ use Dom\NodeList;
+ use Dom\Attr;
+ use Dom\CDATASection;Under the hood, Dom\XMLDocument cannot be instantiated with new. This is handled internally by veewee/xml's loaders, so if you load documents through the Document wrapper, no action is needed.
Only relevant if your code creates DOMDocument instances directly (bypassing the wrapper):
- $document = new DOMDocument();
+ $document = Dom\XMLDocument::createEmpty();
- $document = new DOMDocument();
- $document->loadXML($xml, $options);
+ $document = Dom\XMLDocument::createFromString($xml, $options);
- $document = new DOMDocument();
- $document->load($file, $options);
+ $document = Dom\XMLDocument::createFromFile($file, $options);Both createFromString and createFromFile accept an optional third parameter ?string $override_encoding.
Loaders no longer receive a DOMDocument and mutate it. They now return a new XMLDocument:
- /** @return Closure(DOMDocument): void */
+ /** @return Closure(): XMLDocument */The internal VeeWee\Xml\Dom\Loader\load() function has been removed. Loaders now directly use XMLDocument::createFromString() / createFromFile() wrapped in disallow_issues().
VeeWee\Xml\Dom\Configurator\loader() has been removed. Use Document::fromLoader() instead.
- use function VeeWee\Xml\Dom\Configurator\loader;
- use function VeeWee\Xml\Dom\Loader\xml_string_loader;
-
- Document::configure(
- loader(xml_string_loader($xml)),
- ...configurators
- );
+ use function VeeWee\Xml\Dom\Loader\xml_string_loader;
+
+ Document::fromLoader(
+ xml_string_loader($xml),
+ ...configurators
+ );All static factory methods (fromXmlFile, fromXmlString, fromXmlNode, fromUnsafeDocument) now delegate to the new fromLoader() method:
Document::fromLoader(callable $loader, callable ...$configurators): selfDom\XMLDocument no longer has a preserveWhiteSpace property.
Now re-parses the document with LIBXML_NOBLANKS flag and sets formatOutput = true:
- $document->preserveWhiteSpace = false;
- $document->formatOutput = true;
+ // Use the configurator: pretty_print()
+ // Or manually:
+ $trimmed = XMLDocument::createFromString($xml, LIBXML_NOBLANKS);
+ $trimmed->formatOutput = true;Similarly re-parses with LIBXML_NOBLANKS and sets formatOutput = false.
A new configurator VeeWee\Xml\Dom\Configurator\format_output() was added for simple formatOutput toggling:
use function VeeWee\Xml\Dom\Configurator\format_output;
Document::fromLoader(
xml_file_loader('data.xml', LIBXML_NOBLANKS, 'UTF-8'),
format_output($debug),
);If you use Reader::fromXmlFile() or Reader::fromXmlString(), these continue to work unchanged. The changes below are relevant if you use the loaders directly or the native XMLReader class.
Only relevant if your code uses XMLReader directly (bypassing the veewee Reader wrapper):
- XMLReader::open($file)
+ XMLReader::fromUri($file, $encoding, $flags)
- XMLReader::XML($xml)
+ XMLReader::fromString($xml, $encoding, $flags)use VeeWee\Xml\Reader\Reader;
$reader = Reader::fromXmlStream($stream, ...$configurators);- xml_file_loader(string $file): Closure
+ xml_file_loader(string $file, ?string $encoding = null, int $flags = 0): Closure
- xml_string_loader(string $xml): Closure
+ xml_string_loader(string $xml, ?string $encoding = null, int $flags = 0): ClosureIf you use Writer::inMemory() or Writer::forFile(), these continue to work unchanged. The changes below are relevant if you use custom openers or the native XMLWriter class.
Openers no longer receive an XMLWriter and return bool. They now return a new XMLWriter. Only relevant if you implemented custom openers:
- public function __invoke(XMLWriter $writer): bool;
+ public function __invoke(): XMLWriter;Only relevant if your code uses XMLWriter directly (bypassing the veewee Writer wrapper):
- $writer = new XMLWriter();
- $writer->openMemory();
+ $writer = XMLWriter::toMemory();
- $writer = new XMLWriter();
- $writer->openUri($file);
+ $writer = XMLWriter::toUri($file);VeeWee\Xml\Writer\Configurator\open() has been removed.
- Writer::configure(open(memory_opener()), ...$configurators);
+ Writer::configure(memory_opener(), ...$configurators);use VeeWee\Xml\Writer\Writer;
$writer = Writer::forStream($stream, ...$configurators);use function VeeWee\Xml\Writer\Applicative\flush;
$writer->apply(flush());The functions() configurator now accepts an associative array of callbacks instead of a list of function name strings:
- use function VeeWee\Xml\Dom\Xpath\Configurator\functions;
-
- /** @param non-empty-list<string> $functions */
- functions(['myFunction'])
+ use function VeeWee\Xml\Dom\Xpath\Configurator\functions;
+
+ /** @param array<string, callable(mixed...): mixed> $functions */
+ functions(['myFunction' => my_function(...)])This same change applies to VeeWee\Xml\Xslt\Configurator\functions().
New configurators for registering functions under specific XML namespaces:
// XPath
use function VeeWee\Xml\Dom\Xpath\Configurator\namespaced_functions;
namespaced_functions('http://my-ns.com', 'myprefix', [
'myFunction' => my_function(...),
]);
// XSLT
use function VeeWee\Xml\Xslt\Configurator\namespaced_functions;
namespaced_functions('http://my-ns.com', [
'myFunction' => my_function(...),
]);PHP 8.4 explicit nullable types are now used throughout:
- function query(string $query, DOMNode $node = null): Closure
+ function query(string $query, ?Node $node = null): ClosureThis is not a behavioral change, but may affect static analysis tools.
The new DOM spec provides a native rename() method on elements and attributes.
The library's manipulator functions leverage this internally. No user action required unless you were calling internal manipulation functions directly.
use function VeeWee\Xml\Dom\Manipulator\Xmlns\rename_element_namespace;
rename_element_namespace($element, $namespaceURI, $newPrefix);The implementation changed but the API remains the same. Internally it no longer uses loader():
- Document::configure(
- pretty_print(),
- loader(xml_string_loader($document->C14N(), LIBXML_NSCLEAN + LIBXML_NOCDATA)),
- normalize()
- )
+ Document::fromLoader(
+ xml_string_loader($document->C14N(), LIBXML_NSCLEAN + LIBXML_NOCDATA),
+ pretty_print(),
+ normalize()
+ )No user action required unless you wrote custom configurators following a similar pattern.
| Removed | Replacement |
|---|---|
VeeWee\Xml\Dom\Configurator\loader() |
Use Document::fromLoader() |
VeeWee\Xml\Dom\Loader\load() |
Internal, use disallow_issues() + XMLDocument::createFrom*() |
VeeWee\Xml\Writer\Configurator\open() |
Pass opener directly to Writer::configure() |
| New | Purpose |
|---|---|
Document::fromLoader() |
Load XML via a callable that returns XMLDocument |
Configurator\format_output() |
Toggle formatOutput on document |
Reader::fromXmlStream() |
Read XML from a stream resource |
Reader\Loader\xml_stream_loader() |
Stream-based reader loader |
Writer::forStream() |
Write XML to a stream resource |
Writer\Opener\xml_stream_opener() |
Stream-based writer opener |
Writer\Applicative\flush() |
Flush writer buffer |
Xpath\Configurator\namespaced_functions() |
Register namespaced XPath callback functions |
Xslt\Configurator\namespaced_functions() |
Register namespaced XSLT callback functions |
Manipulator\Xmlns\rename_element_namespace() |
Rename element namespace prefix |
Use this checklist when migrating a project:
- Update
composer.jsonto requirephp: ~8.4.0andveewee/xml: ^4.0 - Search-replace all DOM class references (see table in section 1)
- Replace
new DOMDocument()withXMLDocument::createEmpty(),createFromString(), orcreateFromFile() - Replace
Configurator\loader(xml_*_loader(...))withDocument::fromLoader(xml_*_loader(...)) - Remove
use function VeeWee\Xml\Dom\Configurator\loader - Remove
use function VeeWee\Xml\Dom\Loader\load - Replace
$document->preserveWhiteSpace = falsewithLIBXML_NOBLANKSflag on loaders or usetrim_spaces()/pretty_print()configurators - Update XMLReader:
::open()to::fromUri(),::XML()to::fromString() - Update XMLWriter:
new XMLWriter()+openMemory()/openUri()toXMLWriter::toMemory()/::toUri() - Remove
use function VeeWee\Xml\Writer\Configurator\open - Update
Writer::configure()calls: pass opener as first argument directly - Update
functions()configurator calls: change from['funcName']to['funcName' => $callback] - Update nullable parameter syntax if your static analysis requires it
- Run static analysis and tests to catch any remaining type mismatches