Skip to content

Commit

Permalink
WiP
Browse files Browse the repository at this point in the history
  • Loading branch information
JoshyPHP committed Dec 28, 2023
1 parent a275f0c commit b5c00ab
Show file tree
Hide file tree
Showing 2 changed files with 139 additions and 23 deletions.
51 changes: 28 additions & 23 deletions src/NodeComparator.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,14 @@
use DOMAttr;
use DOMCharacterData;
use DOMDocument;
use DOMDocumentFragment;
use DOMDocumentType;
use DOMElement;
use DOMEntity;
use DOMEntityReference;
use DOMNode;
use DOMNodeList;
use DOMNotation;
use DOMProcessingInstruction;
use DOMXPath;

Expand All @@ -23,7 +27,7 @@ class NodeComparator
// https://github.com/php/php-src/blob/master/ext/dom/node.c
public static function isEqualNode(?DOMNode $node, ?DOMNode $otherNode): bool
{
if (!isset($node, $otherNode) || $node->nodeType !== $otherNode->nodeType)
if (!isset($node, $otherNode) || $node->nodeType !== $otherNode->nodeType || $node->nodeName !== $otherNode->nodeName)
{
return false;
}
Expand All @@ -45,9 +49,10 @@ public static function isEqualNode(?DOMNode $node, ?DOMNode $otherNode): bool
&& $node->localName === $otherNode->localName
&& $node->value === $otherNode->value;
}
if ($node instanceof DOMDocument && $otherNode instanceof DOMDocument)
if (($node instanceof DOMDocument && $otherNode instanceof DOMDocument)
|| ($node instanceof DOMDocumentFragment && $otherNode instanceof DOMDocumentFragment))
{
return self::isEqualDocumentNode($node, $otherNode);
return self::isEqualNodeList($node->childNodes, $otherNode->childNodes);
}
if ($node instanceof DOMDocumentType && $otherNode instanceof DOMDocumentType)
{
Expand All @@ -59,6 +64,13 @@ public static function isEqualNode(?DOMNode $node, ?DOMNode $otherNode): bool
{
return $node->nodeName === $otherNode->nodeName;
}
if (($node instanceof DOMEntity && $otherNode instanceof DOMEntity)
|| ($node instanceof DOMNotation && $otherNode instanceof DOMNotation))
{
return $node->nodeName === $otherNode->nodeName
&& $node->publicId === $otherNode->publicId
&& $node->systemId === $otherNode->systemId;
}

return $node->isSameNode($otherNode);
}
Expand Down Expand Up @@ -86,23 +98,6 @@ protected static function hasEqualNamespaceDeclarations(DOMElement $element, DOM
return self::getNamespaceDeclarations($element) == self::getNamespaceDeclarations($otherElement);
}

protected static function isEqualDocumentNode(DOMDocument $document, DOMDocument $otherDocument): bool
{
if ($document->childNodes->length !== $otherDocument->childNodes->length)
{
return false;
}
foreach ($document->childNodes as $i => $childNode)
{
if (!self::isEqualNode($childNode, $otherDocument->childNodes[$i]))
{
return false;
}
}

return true;
}

protected static function isEqualElementNode(DOMElement $element, DOMElement $otherElement): bool
{
if ($element->namespaceURI !== $otherElement->namespaceURI
Expand All @@ -121,14 +116,24 @@ protected static function isEqualElementNode(DOMElement $element, DOMElement $ot
}
}

foreach ($element->childNodes as $i => $childNode)
return self::isEqualNodeList($element->childNodes, $otherElement->childNodes)
&& self::hasEqualNamespaceDeclarations($element, $otherElement);
}

protected static function isEqualNodeList(DOMNodeList $list, DOMNodeList $otherList): bool
{
if ($list->length !== $otherList->length)
{
return false;
}
foreach ($list as $i => $node)
{
if (!self::isEqualNode($childNode, $otherElement->childNodes[$i]))
if (!self::isEqualNode($node, $otherList[$i]))
{
return false;
}
}

return self::hasEqualNamespaceDeclarations($element, $otherElement);
return true;
}
}
111 changes: 111 additions & 0 deletions tests/NodeComparatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@ public static function getIsEqualNodeCases(): array
'<y/>',
'//y'
],
[
false,
'<x/>',
'//x',
'<X/>',
'//X'
],
[
true,
'<x a="0"/>',
Expand Down Expand Up @@ -292,6 +299,24 @@ public function testIsEqualDocumentNode()
$this->assertIsEqualNode(true, new DOMDocument, new DOMDocument);
}

public function testIsEqualDocumentNodeClone()
{
$dom1 = new DOMDocument;
$dom1->loadXML(<<<'EOT'
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd" [
<!ENTITY bar '<bar>bartext</bar>'>
<!ENTITY foo '<foo/>'>
<!NOTATION myNotation SYSTEM "test.dtd">
]>
<html>
<body>
<p>...</p>
</body>
</html>
EOT);
$this->assertIsEqualNode(true, $dom1, clone $dom1);
}

public function testIsEqualDocumentTypeNode()
{
$dom1 = new DOMDocument;
Expand Down Expand Up @@ -325,4 +350,90 @@ public function testIsEqualEntityReference()
$this->assertIsEqualNode(false, new DOMEntityReference('ref'), new DOMEntityReference('ref2'));
$this->assertIsEqualNode(true, new DOMEntityReference('ref'), new DOMEntityReference('ref'));
}

public function testIsEqualEntityDeclarationNode()
{
$dom1 = new DOMDocument;
$dom1->loadXML(<<<'EOT'
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd" [
<!ENTITY bar '<bar>bartext</bar>'>
<!ENTITY foo '<foo/>'>
<!NOTATION myNotation SYSTEM "test.dtd">
]>
<html>
<body>
<p>...</p>
</body>
</html>
EOT);

$dom2 = new DOMDocument;
$dom2->loadXML(<<<'EOT'
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd" [
<!ENTITY barbar '<bar>bartext</bar>'>
<!ENTITY foo '<foo2/>'>
<!ENTITY bar '<bar>bartext</bar>'>
]>
<html>
<body>
<p>...</p>
</body>
</html>
EOT);

$this->assertIsEqualNode(true, $dom1->doctype->entities['bar'], $dom2->doctype->entities['bar']);
$this->assertIsEqualNode(false, $dom1->doctype->entities['bar'], $dom2->doctype->entities['barbar']);
$this->assertIsEqualNode(false, $dom1->doctype->entities['bar'], $dom2->doctype->entities['foo']);
$this->assertIsEqualNode(false, $dom1->doctype->entities['foo'], $dom2->doctype->entities['bar']);
$this->assertIsEqualNode(false, $dom1->doctype->entities['foo'], $dom2->doctype->entities['barbar']);
$this->assertIsEqualNode(true, $dom1->doctype->entities['foo'], $dom2->doctype->entities['foo']);
}

public function testIsEqualEntityNotationNode()
{
$dom1 = new DOMDocument;
$dom1->loadXML(<<<'EOT'
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd" [
<!ENTITY bar '<bar>bartext</bar>'>
<!ENTITY foo '<foo/>'>
<!NOTATION myNotation SYSTEM "test.dtd">
]>
<html>
<body>
<p>...</p>
</body>
</html>
EOT);

$dom2 = new DOMDocument;
$dom2->loadXML(<<<'EOT'
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd" [
<!NOTATION myNotation SYSTEM "test.dtd">
<!NOTATION myNotation2 SYSTEM "test2.dtd">
<!NOTATION myNotation3 SYSTEM "test.dtd">
]>
<html><body><p>...</p></body></html>
EOT);

$this->assertIsEqualNode(true, $dom1->doctype->notations['myNotation'], $dom2->doctype->notations['myNotation']);
$this->assertIsEqualNode(false, $dom1->doctype->notations['myNotation'], $dom2->doctype->notations['myNotation2']);
$this->assertIsEqualNode(false, $dom1->doctype->notations['myNotation'], $dom2->doctype->notations['myNotation3']);
}

public function testIsEqualDocumentFragmentNode()
{
$xml = '<x><y/></x><z/>';

$dom = new DOMDocument;
$frag1 = $dom->createDocumentFragment();
$frag2 = $dom->createDocumentFragment();
$frag3 = $dom->createDocumentFragment();

$frag1->appendXML($xml);
$frag2->appendXML($xml);
$frag3->appendXML('<x/><z/>');

$this->assertIsEqualNode(true, $frag1, $frag2);
$this->assertIsEqualNode(false, $frag1, $frag3);
}
}

0 comments on commit b5c00ab

Please sign in to comment.