diff --git a/src/DOMNodeComparator.php b/src/DOMNodeComparator.php index 5b8629ca..fa642abe 100644 --- a/src/DOMNodeComparator.php +++ b/src/DOMNodeComparator.php @@ -70,8 +70,15 @@ public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = f private function nodeToText(DOMNode $node, bool $canonicalize, bool $ignoreCase): string { if ($canonicalize) { + /** @psalm-var string|false $c14n */ + $c14n = @$node->C14N(); + + if ($c14n === false || $c14n === '') { // try node-to-text without canonicalize + return $this->nodeToText($node, false, $ignoreCase); + } + $document = new DOMDocument; - @$document->loadXML($node->C14N()); + $document->loadXML($c14n); $node = $document; } diff --git a/tests/DOMNodeComparatorTest.php b/tests/DOMNodeComparatorTest.php index 8d52fc2b..6a30e0c0 100644 --- a/tests/DOMNodeComparatorTest.php +++ b/tests/DOMNodeComparatorTest.php @@ -121,6 +121,50 @@ public function assertEqualsFailsProvider() ]; } + public function assertEqualsSucceedsWithNonCanonicalizableNodesProvider() + { + $document = new DOMDocument; + + return [ + [$document, new DOMDocument], + [$document->createElement('foo'), $document->createElement('foo')], + [ + $this->createDOMDocument( + ']>' + ), + $this->createDOMDocument( + ']>' + ), + ], + ]; + } + + public function assertEqualsFailsWithNonCanonicalizableNodesProvider() + { + // empty document makes C14N return empty string + $document = new DOMDocument; + + // nodes created but not appended to the document makes C14N return empty string + $nodeFoo = $document->createElement('foo'); + $nodeBar = $document->createElement('bar'); + + // documents with xmlns definitions to xml entities makes C14N return false + $documentNsUriIsEntityFoo = $this->createDOMDocument( // root element is i:foo + ']>' + ); + $documentNsUriIsEntityBar = $this->createDOMDocument( // root element is i:bar + ']>' + ); + + return [ + [$document, $nodeFoo], + [$document, $documentNsUriIsEntityFoo], + [$nodeFoo, $nodeBar], + [$nodeFoo, $documentNsUriIsEntityFoo], + [$documentNsUriIsEntityFoo, $documentNsUriIsEntityBar], + ]; + } + /** * @dataProvider acceptsSucceedsProvider */ @@ -169,6 +213,22 @@ public function testAssertEqualsFails($expected, $actual): void $this->comparator->assertEquals($expected, $actual); } + /** + * @dataProvider assertEqualsSucceedsWithNonCanonicalizableNodesProvider + */ + public function testAssertEqualsSucceedsWithNonCanonicalizableNodes($expected, $actual): void + { + $this->testAssertEqualsSucceeds($expected, $actual); + } + + /** + * @dataProvider assertEqualsFailsWithNonCanonicalizableNodesProvider + */ + public function testAssertEqualsFailsWithNonCanonicalizableNodes($expected, $actual): void + { + $this->testAssertEqualsFails($expected, $actual); + } + private function createDOMDocument($content) { $document = new DOMDocument;