Для будущих искателей: вот такой монстрик у меня получился (PHP 7.1). Он проходит все проверки из МР 3.5, но, возможно, некоторых нюансов не учитывает.
<?php
$in = new DOMDocument();
$in->load($argv[1]);
$out = new XMLWriter();
$out->openMemory();
$index = 0;
$stack = [null, [$in->documentElement, []]];
while (count($stack)) {
$item = array_pop($stack);
if ($item === null) {
$out->text('');
$out->endElement();
continue;
}
[$node, $nsList] = $item;
if ($node->nodeType == XML_ELEMENT_NODE) {
// Let the magic begin! ;)
// The element itself.
if ($node->namespaceURI !== null) {
$thisLevel = false;
if (($nsList[$node->namespaceURI] ?? null) === null) {
$thisLevel = true;
$nsList[$node->namespaceURI] = 'ns' . ++$index;
}
$out->startElement("{$nsList[$node->namespaceURI]}:{$node->localName}");
if ($thisLevel) {
$out->writeAttribute("xmlns:{$nsList[$node->namespaceURI]}", $node->namespaceURI);
}
}
else {
$out->startElement($node->localName);
}
// Attributes.
$attrs = iterator_to_array($node->attributes);
usort($attrs, function($a, $b) {
if ($a->namespaceURI !== null && $b->namespaceURI === null) return -1;
else if ($a->namespaceURI === null && $b->namespaceURI !== null) return 1;
else return strcmp($a->namespaceURI, $b->namespaceURI) ?: strcmp($a->localName, $b->localName);
});
foreach ($attrs as $attr) {
if ($attr->namespaceURI !== null && ($nsList[$attr->namespaceURI] ?? null) === null) {
$nsList[$attr->namespaceURI] = 'ns' . ++$index;
$out->writeAttribute("xmlns:{$nsList[$attr->namespaceURI]}", $attr->namespaceURI);
}
}
foreach ($attrs as $attr) {
if ($attr->namespaceURI !== null) {
$out->writeAttribute("{$nsList[$attr->namespaceURI]}:{$attr->localName}", $attr->nodeValue);
}
else {
$out->writeAttribute($attr->localName, $attr->nodeValue);
}
}
}
else if ($node->nodeType == XML_TEXT_NODE && strlen(trim($node->nodeValue))) {
$out->text($node->nodeValue);
}
if ($node->lastChild !== null) {
$stack[] = null;
for ($node = $node->lastChild; $node !== null; $node = $node->previousSibling) {
$stack[] = [$node, $nsList];
}
}
}
echo $out->outputMemory(), "\n";
?>