Skip to content

Commit

Permalink
Inherit XMLNS during encoding like we did in v3
Browse files Browse the repository at this point in the history
  • Loading branch information
veewee committed Jan 22, 2025
1 parent 86a8497 commit b5debff
Show file tree
Hide file tree
Showing 11 changed files with 83 additions and 18 deletions.
7 changes: 3 additions & 4 deletions src/Xml/Dom/Builder/nodes.php
Original file line number Diff line number Diff line change
Expand Up @@ -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<callable(XMLDocument): (list<Node>|Node)> $builders
* @param list<callable(Node): (list<Node>|Node)> $builders
*
* @return Closure(XMLDocument): list<Node>
* @return Closure(Node): list<Node>
*/
function nodes(callable ... $builders): Closure
{
Expand All @@ -27,7 +26,7 @@ function nodes(callable ... $builders): Closure
$builders,
/**
* @param list<Node> $builds
* @param callable(XMLDocument): (Node|list<Node>) $builder
* @param callable(Node): (Node|list<Node>) $builder
* @return list<Node>
*/
static function (array $builds, callable $builder) use ($node): array {
Expand Down
2 changes: 2 additions & 0 deletions src/Xml/Dom/Document.php
Original file line number Diff line number Diff line change
Expand Up @@ -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<callable(XMLDocument): (list<Node>|Node)> $builders
*
* @return list<Node>
Expand Down
18 changes: 18 additions & 0 deletions src/Xml/Dom/Locator/Xmlns/closest_unprefixed_xmlns.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace VeeWee\Xml\Dom\Locator\Xmlns;

use Dom\Element;

function closest_unprefixed_xmlns(Element $node): ?string
{
foreach (linked_namespaces($node) as $namespace) {
if ($namespace->prefix === null) {
return $namespace->namespaceURI;
}
}

return null;
}
16 changes: 16 additions & 0 deletions src/Xml/Dom/Predicate/is_prefixed_node_name.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace VeeWee\Xml\Dom\Predicate;

use function Psl\Result\wrap;
use function VeeWee\Xml\Assertion\assert_strict_prefixed_name;

function is_prefixed_node_name(string $nodeName): bool
{
return wrap(static fn () => assert_strict_prefixed_name($nodeName))->proceed(
static fn() => true,
static fn() => false,
);
}
9 changes: 3 additions & 6 deletions src/Xml/Encoding/Internal/Decoder/Builder/namespaces.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
]),
[]
),
]);
Expand Down
2 changes: 1 addition & 1 deletion src/Xml/Encoding/Internal/Encoder/Builder/children.php
Original file line number Diff line number Diff line change
Expand Up @@ -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)])
)
);
}
6 changes: 1 addition & 5 deletions src/Xml/Encoding/Internal/Encoder/Builder/element.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand Down Expand Up @@ -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);
}
2 changes: 1 addition & 1 deletion src/Xml/Encoding/Internal/Encoder/Builder/parent_node.php
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

declare(strict_types=1);

namespace VeeWee\Xml\Encoding\Internal\Encoder\Builder;

use Closure;
use Dom\Element;
use Dom\XMLDocument;
use VeeWee\Xml\Xmlns\Xmlns;
use function VeeWee\Xml\Dom\Builder\element as elementBuilder;
use function VeeWee\Xml\Dom\Builder\namespaced_element as namespacedElementBuilder;
use function VeeWee\Xml\Dom\Locator\Xmlns\closest_unprefixed_xmlns;
use function VeeWee\Xml\Dom\Predicate\is_element;
use function VeeWee\Xml\Dom\Predicate\is_prefixed_node_name;

/**
* This function can create element nodes that inherit the local xmlns namespace of their parent if none is configured.
*
* @param list<Closure(Element): Element> $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);
};
}
3 changes: 3 additions & 0 deletions src/bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -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',
Expand Down
2 changes: 1 addition & 1 deletion tests/Xml/Encoding/EncodingTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ public static function provideRiskyBidirectionalCases()
yield 'namespaced' => [
'xml' => <<<EOXML
<root xmlns="http://rooty.root" xmlns:test="http://testy.test">
<test:item xmlns="">
<test:item>
<id:int xmlns:id="http://identity.id">1</id:int>
</test:item>
</root>
Expand Down

0 comments on commit b5debff

Please sign in to comment.