From b5debff712c82dd6db0ed0998a0dd958f9c5beb3 Mon Sep 17 00:00:00 2001 From: Toon Verwerft Date: Wed, 22 Jan 2025 16:29:29 +0100 Subject: [PATCH] Inherit XMLNS during encoding like we did in v3 --- src/Xml/Dom/Builder/nodes.php | 7 ++-- src/Xml/Dom/Document.php | 2 ++ .../Xmlns/closest_unprefixed_xmlns.php | 18 ++++++++++ .../Dom/Predicate/is_prefixed_node_name.php | 16 +++++++++ .../Internal/Decoder/Builder/namespaces.php | 9 ++--- .../Internal/Encoder/Builder/children.php | 2 +- .../Internal/Encoder/Builder/element.php | 6 +--- .../Internal/Encoder/Builder/parent_node.php | 2 +- .../Builder/xmlns_inheriting_element.php | 34 +++++++++++++++++++ src/bootstrap.php | 3 ++ tests/Xml/Encoding/EncodingTest.php | 2 +- 11 files changed, 83 insertions(+), 18 deletions(-) create mode 100644 src/Xml/Dom/Locator/Xmlns/closest_unprefixed_xmlns.php create mode 100644 src/Xml/Dom/Predicate/is_prefixed_node_name.php create mode 100644 src/Xml/Encoding/Internal/Encoder/Builder/xmlns_inheriting_element.php diff --git a/src/Xml/Dom/Builder/nodes.php b/src/Xml/Dom/Builder/nodes.php index 82560964..7d399a0c 100644 --- a/src/Xml/Dom/Builder/nodes.php +++ b/src/Xml/Dom/Builder/nodes.php @@ -6,15 +6,14 @@ use Closure; use Dom\Node; -use Dom\XMLDocument; use function is_array; use function Psl\Iter\reduce; use function VeeWee\Xml\Dom\Locator\Node\detect_document; /** - * @param list|Node)> $builders + * @param list|Node)> $builders * - * @return Closure(XMLDocument): list + * @return Closure(Node): list */ function nodes(callable ... $builders): Closure { @@ -27,7 +26,7 @@ function nodes(callable ... $builders): Closure $builders, /** * @param list $builds - * @param callable(XMLDocument): (Node|list) $builder + * @param callable(Node): (Node|list) $builder * @return list */ static function (array $builds, callable $builder) use ($node): array { diff --git a/src/Xml/Dom/Document.php b/src/Xml/Dom/Document.php index 74b08c92..38c35b1b 100644 --- a/src/Xml/Dom/Document.php +++ b/src/Xml/Dom/Document.php @@ -130,6 +130,8 @@ public function manipulate(callable $manipulator): self } /** + * @psalm-suppress ArgumentTypeCoercion - nodes() works on node but we provide the parent type XMLDocument. + * * @param list|Node)> $builders * * @return list diff --git a/src/Xml/Dom/Locator/Xmlns/closest_unprefixed_xmlns.php b/src/Xml/Dom/Locator/Xmlns/closest_unprefixed_xmlns.php new file mode 100644 index 00000000..23851c07 --- /dev/null +++ b/src/Xml/Dom/Locator/Xmlns/closest_unprefixed_xmlns.php @@ -0,0 +1,18 @@ +prefix === null) { + return $namespace->namespaceURI; + } + } + + return null; +} diff --git a/src/Xml/Dom/Predicate/is_prefixed_node_name.php b/src/Xml/Dom/Predicate/is_prefixed_node_name.php new file mode 100644 index 00000000..52c2bc31 --- /dev/null +++ b/src/Xml/Dom/Predicate/is_prefixed_node_name.php @@ -0,0 +1,16 @@ + assert_strict_prefixed_name($nodeName))->proceed( + static fn() => true, + static fn() => false, + ); +} diff --git a/src/Xml/Encoding/Internal/Decoder/Builder/namespaces.php b/src/Xml/Encoding/Internal/Decoder/Builder/namespaces.php index 19d3a484..6c77dc3e 100644 --- a/src/Xml/Encoding/Internal/Decoder/Builder/namespaces.php +++ b/src/Xml/Encoding/Internal/Decoder/Builder/namespaces.php @@ -20,12 +20,9 @@ function namespaces(Element $element): array { return filter([ '@namespaces' => xmlns_attributes_list($element)->reduce( - static fn (array $namespaces, Attr $node) - => $node->value - ? merge($namespaces, [ - ($node->prefix !== null ? $node->localName : '') => $node->value - ]) - : $namespaces, + static fn (array $namespaces, Attr $node) => merge($namespaces, [ + ($node->prefix !== null ? $node->localName : '') => $node->value + ]), [] ), ]); diff --git a/src/Xml/Encoding/Internal/Encoder/Builder/children.php b/src/Xml/Encoding/Internal/Encoder/Builder/children.php index 96227bc1..9bbba7a2 100644 --- a/src/Xml/Encoding/Internal/Encoder/Builder/children.php +++ b/src/Xml/Encoding/Internal/Encoder/Builder/children.php @@ -28,7 +28,7 @@ function children(string $name, array $children): Closure */ static fn (array|string $data): Closure => is_array($data) ? element($name, $data) - : elementBuilder($name, value($data)) + : xmlns_inheriting_element($name, [value($data)]) ) ); } diff --git a/src/Xml/Encoding/Internal/Encoder/Builder/element.php b/src/Xml/Encoding/Internal/Encoder/Builder/element.php index 901fa3f7..bcb27bc8 100644 --- a/src/Xml/Encoding/Internal/Encoder/Builder/element.php +++ b/src/Xml/Encoding/Internal/Encoder/Builder/element.php @@ -19,9 +19,7 @@ use function VeeWee\Xml\Dom\Builder\attributes; use function VeeWee\Xml\Dom\Builder\cdata; use function VeeWee\Xml\Dom\Builder\children as childrenBuilder; -use function VeeWee\Xml\Dom\Builder\element as elementBuilder; use function VeeWee\Xml\Dom\Builder\escaped_value; -use function VeeWee\Xml\Dom\Builder\namespaced_element as namespacedElementBuilder; use function VeeWee\Xml\Dom\Builder\xmlns_attributes; /** @@ -66,7 +64,5 @@ function element(string $name, array $data): Closure )), ]); - return $currentNamespace !== null - ? namespacedElementBuilder($currentNamespace, $name, ...$children) - : elementBuilder($name, ...$children); + return xmlns_inheriting_element($name, $children, $currentNamespace); } diff --git a/src/Xml/Encoding/Internal/Encoder/Builder/parent_node.php b/src/Xml/Encoding/Internal/Encoder/Builder/parent_node.php index 89b52364..bef127b3 100644 --- a/src/Xml/Encoding/Internal/Encoder/Builder/parent_node.php +++ b/src/Xml/Encoding/Internal/Encoder/Builder/parent_node.php @@ -25,7 +25,7 @@ function parent_node(string $name, array|string $data): Closure { if (is_string($data)) { - return buildChildren(elementBuilder($name, escaped_value($data))); + return buildChildren(xmlns_inheriting_element($name, [escaped_value($data)])); } if (is_node_list($data)) { diff --git a/src/Xml/Encoding/Internal/Encoder/Builder/xmlns_inheriting_element.php b/src/Xml/Encoding/Internal/Encoder/Builder/xmlns_inheriting_element.php new file mode 100644 index 00000000..63b5e483 --- /dev/null +++ b/src/Xml/Encoding/Internal/Encoder/Builder/xmlns_inheriting_element.php @@ -0,0 +1,34 @@ + $children + * @return Closure(Element): Element + */ +function xmlns_inheriting_element(string $name, array $children, ?string $localNamespace = null): Closure +{ + return function (XMLDocument|Element $parent) use ($localNamespace, $name, $children): Element { + if ($localNamespace === null && is_element($parent) && !is_prefixed_node_name($name)) { + $localNamespace = closest_unprefixed_xmlns($parent); + } + + return $localNamespace !== null + ? namespacedElementBuilder($localNamespace, $name, ...$children)($parent) + : elementBuilder($name, ...$children)($parent); + }; +} diff --git a/src/bootstrap.php b/src/bootstrap.php index 2a459941..4b7b8eab 100644 --- a/src/bootstrap.php +++ b/src/bootstrap.php @@ -47,6 +47,7 @@ 'Xml\Dom\Locator\Node\children' => __DIR__.'/Xml/Dom/Locator/Node/children.php', 'Xml\Dom\Locator\Node\detect_document' => __DIR__.'/Xml/Dom/Locator/Node/detect_document.php', 'Xml\Dom\Locator\Node\value' => __DIR__.'/Xml/Dom/Locator/Node/value.php', + 'Xml\Dom\Locator\Xmlns\closest_unprefixed_xmlns' => __DIR__ . '/Xml/Dom/Locator/Xmlns/closest_unprefixed_xmlns.php', 'Xml\Dom\Locator\Xmlns\linked_namespaces' => __DIR__.'/Xml/Dom/Locator/Xmlns/linked_namespaces.php', 'Xml\Dom\Locator\Xmlns\recursive_linked_namespaces' => __DIR__.'/Xml/Dom/Locator/Xmlns/recursive_linked_namespaces.php', 'Xml\Dom\Locator\Xsd\locate_all_xsd_schemas' => __DIR__.'/Xml/Dom/Locator/Xsd/locate_all_xsd_schemas.php', @@ -79,6 +80,7 @@ 'Xml\Dom\Predicate\is_document_element' => __DIR__.'/Xml/Dom/Predicate/is_document_element.php', 'Xml\Dom\Predicate\is_element' => __DIR__.'/Xml/Dom/Predicate/is_element.php', 'Xml\Dom\Predicate\is_non_empty_text' => __DIR__.'/Xml/Dom/Predicate/is_non_empty_text.php', + 'Xml\Dom\Predicate\is_prefixed_node_name' => __DIR__.'/Xml/Dom/Predicate/is_prefixed_node_name.php', 'Xml\Dom\Predicate\is_text' => __DIR__.'/Xml/Dom/Predicate/is_text.php', 'Xml\Dom\Predicate\is_whitespace' => __DIR__.'/Xml/Dom/Predicate/is_whitespace.php', 'Xml\Dom\Predicate\is_xmlns_attribute' => __DIR__.'/Xml/Dom/Predicate/is_xmlns_attribute.php', @@ -107,6 +109,7 @@ 'Xml\Encoding\Internal\Encoder\Builder\normalize_data' => __DIR__.'/Xml/Encoding/Internal/Encoder/Builder/normalize_data.php', 'Xml\Encoding\Internal\Encoder\Builder\parent_node' => __DIR__.'/Xml/Encoding/Internal/Encoder/Builder/parent_node.php', 'Xml\Encoding\Internal\Encoder\Builder\root' => __DIR__.'/Xml/Encoding/Internal/Encoder/Builder/root.php', + 'Xml\Encoding\Internal\Encoder\Builder\xmlns_inheriting_element' => __DIR__.'/Xml/Encoding/Internal/Encoder/Builder/xmlns_inheriting_element.php', 'Xml\Encoding\Internal\wrap_exception' => __DIR__.'/Xml/Encoding/Internal/wrap_exception.php', 'Xml\Encoding\document_encode' => __DIR__.'/Xml/Encoding/document_encode.php', 'Xml\Encoding\element_decode' => __DIR__.'/Xml/Encoding/element_decode.php', diff --git a/tests/Xml/Encoding/EncodingTest.php b/tests/Xml/Encoding/EncodingTest.php index d6947329..ed6e4f9d 100644 --- a/tests/Xml/Encoding/EncodingTest.php +++ b/tests/Xml/Encoding/EncodingTest.php @@ -250,7 +250,7 @@ public static function provideRiskyBidirectionalCases() yield 'namespaced' => [ 'xml' => << - + 1