Skip to content

Commit

Permalink
Make namespace remover configurable
Browse files Browse the repository at this point in the history
  • Loading branch information
veewee committed Nov 5, 2023
1 parent 8103352 commit a57594d
Show file tree
Hide file tree
Showing 3 changed files with 167 additions and 2 deletions.
10 changes: 10 additions & 0 deletions docs/dom.md
Original file line number Diff line number Diff line change
Expand Up @@ -1385,6 +1385,16 @@ $doc = Document::fromXmlFile($file);
$doc->traverse(new Visitor\RemoveNamespaces());
```

This visitor allows some additional configurations:

* `RemoveNamespaces:all()`: Removes all namespaces by default.
* `RemoveNamespaces::prefixed()`: Removes all namespaces with a prefix (`xmlns:prefix=""`).
* `RemoveNamespaces::unprefixed()`: Removes all namespaces without a prefix (`xmlns=""`).
* `RemoveNamespaces::byPrefixNames(['a', 'b'])`: Removes all namespaces with a specific prefix.
* `RemoveNamespaces::byNamespaceURIs(['http://xxx'])`: Removes all namespaces with a specific namespace URI.
* `new RemoveNamespaces($yourFilter)`: If you want to apply a custom filter to select the namespaces you allow to be stripped.


#### Building your own visitor

A visitor needs to implement the visitor interface.
Expand Down
59 changes: 59 additions & 0 deletions src/Xml/Dom/Traverser/Visitor/RemoveNamespaces.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,69 @@

namespace VeeWee\Xml\Dom\Traverser\Visitor;

use DOMNameSpaceNode;
use DOMNode;
use VeeWee\Xml\Dom\Traverser\Action;
use VeeWee\Xml\Exception\RuntimeException;
use function Psl\Iter\contains;
use function VeeWee\Xml\Dom\Locator\Attribute\xmlns_attributes_list;
use function VeeWee\Xml\Dom\Predicate\is_element;

final class RemoveNamespaces extends AbstractVisitor
{
/**
* @var null | callable(DOMNameSpaceNode): bool
*/
private $filter;

/**
* @param null | callable(DOMNameSpaceNode): bool $filter
*/
public function __construct(
?callable $filter = null
) {
$this->filter = $filter;
}

public static function all(): self
{
return new self();
}

public static function prefixed(): self
{
return new self(
static fn (DOMNameSpaceNode $node): bool => $node->prefix !== ''
);
}

public static function unprefixed(): self
{
return new self(
static fn (DOMNameSpaceNode $node): bool => $node->prefix === ''
);
}

/**
* @param list<string> $prefixes
*/
public static function byPrefixNames(array $prefixes): self
{
return new self(
static fn (DOMNameSpaceNode $node): bool => contains($prefixes, $node->prefix)
);
}

/**
* @param list<string> $URIs
*/
public static function byNamespaceURIs(array $URIs): self
{
return new self(
static fn (DOMNameSpaceNode $node): bool => contains($URIs, $node->namespaceURI)
);
}

/**
* @throws RuntimeException
*/
Expand All @@ -21,6 +76,10 @@ public function onNodeLeave(DOMNode $node): Action
}

$namespaces = xmlns_attributes_list($node);
if ($this->filter) {
$namespaces = $namespaces->filter($this->filter);
}

foreach ($namespaces as $namespace) {
$node->removeAttributeNS(
$namespace->namespaceURI,
Expand Down
100 changes: 98 additions & 2 deletions tests/Xml/Dom/Traverser/Visitor/RemoveNamespacesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

final class RemoveNamespacesTest extends TestCase
{
public function test_it_can_sort_attributes(): void
public function test_it_can_remove_all_namespaces(): void
{
$in = <<<EOXML
<hello xmlns:a="http/a" xmlns:z="http/z" version="1.9" target="universe">
Expand All @@ -30,7 +30,103 @@ public function test_it_can_sort_attributes(): void
EOXML;

$doc = Document::fromXmlString($in);
$result = $doc->traverse(new RemoveNamespaces());
$result = $doc->traverse(RemoveNamespaces::all());

static::assertSame($expected, xml_string()($result));
}

public function test_it_can_remove_unprefixed_namespaces(): void
{
$in = <<<EOXML
<hello xmlns:a="http/a" xmlns:z="http/z" version="1.9" target="universe">
<item xmlns="http://raw" id="1" sku="jos">Jos</item>
<x:item xmlns:x="http://x" sku="jaak" id="2">Jaak</x:item>
<item a:sku="jaak" z:id="3">Jul</item>
</hello>
EOXML;

$expected = <<<EOXML
<hello xmlns:a="http/a" xmlns:z="http/z" version="1.9" target="universe">
<item id="1" sku="jos">Jos</item>
<x:item xmlns:x="http://x" sku="jaak" id="2">Jaak</x:item>
<item a:sku="jaak" z:id="3">Jul</item>
</hello>
EOXML;

$doc = Document::fromXmlString($in);
$result = $doc->traverse(RemoveNamespaces::unprefixed());

static::assertSame($expected, xml_string()($result));
}

public function test_it_can_remove_prefixed_namespaces(): void
{
$in = <<<EOXML
<hello xmlns:a="http/a" xmlns:z="http/z" version="1.9" target="universe">
<item xmlns="http://raw" id="1" sku="jos">Jos</item>
<x:item xmlns:x="http://x" sku="jaak" id="2">Jaak</x:item>
<item a:sku="jaak" z:id="3">Jul</item>
</hello>
EOXML;

$expected = <<<EOXML
<hello version="1.9" target="universe">
<item xmlns="http://raw" id="1" sku="jos">Jos</item>
<item sku="jaak" id="2">Jaak</item>
<item sku="jaak" id="3">Jul</item>
</hello>
EOXML;

$doc = Document::fromXmlString($in);
$result = $doc->traverse(RemoveNamespaces::prefixed());

static::assertSame($expected, xml_string()($result));
}

public function test_it_can_remove_namespaces_by_prefix_name(): void
{
$in = <<<EOXML
<hello xmlns:a="http/a" xmlns:z="http/z" version="1.9" target="universe">
<item xmlns="http://raw" id="1" sku="jos">Jos</item>
<x:item xmlns:x="http://x" sku="jaak" id="2">Jaak</x:item>
<item a:sku="jaak" z:id="3">Jul</item>
</hello>
EOXML;

$expected = <<<EOXML
<hello version="1.9" target="universe">
<item id="1" sku="jos">Jos</item>
<x:item xmlns:x="http://x" sku="jaak" id="2">Jaak</x:item>
<item sku="jaak" id="3">Jul</item>
</hello>
EOXML;

$doc = Document::fromXmlString($in);
$result = $doc->traverse(RemoveNamespaces::byPrefixNames(['', 'a', 'z']));

static::assertSame($expected, xml_string()($result));
}

public function test_it_can_remove_namespaces_by_prefix__uri(): void
{
$in = <<<EOXML
<hello xmlns:a="http/a" xmlns:z="http/z" version="1.9" target="universe">
<item xmlns="http://raw" id="1" sku="jos">Jos</item>
<x:item xmlns:x="http://x" sku="jaak" id="2">Jaak</x:item>
<item a:sku="jaak" z:id="3">Jul</item>
</hello>
EOXML;

$expected = <<<EOXML
<hello xmlns:z="http/z" version="1.9" target="universe">
<item id="1" sku="jos">Jos</item>
<x:item xmlns:x="http://x" sku="jaak" id="2">Jaak</x:item>
<item sku="jaak" z:id="3">Jul</item>
</hello>
EOXML;

$doc = Document::fromXmlString($in);
$result = $doc->traverse(RemoveNamespaces::byNamespaceURIs(['http/a', 'http://raw']));

static::assertSame($expected, xml_string()($result));
}
Expand Down

0 comments on commit a57594d

Please sign in to comment.