1--TEST--
2DOMNode::isEqualNode()
3--EXTENSIONS--
4dom
5--SKIPIF--
6<?php
7if (LIBXML_VERSION >= 21200 && LIBXML_VERSION <= 21201) {
8    die("xfail Broken for libxml2 2.12.0 - 2.12.1 see https://gitlab.gnome.org/GNOME/libxml2/-/issues/634");
9}
10?>
11--FILE--
12<?php
13
14$dom1 = new DOMDocument();
15$dom2 = new DOMDocument();
16
17$dom1->loadXML(<<<XML
18<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd" [
19    <!ENTITY bar '<bar>bartext</bar>'>
20    <!ENTITY foo '<foo/>'>
21    <!NOTATION myNotation SYSTEM "test.dtd">
22]>
23<html>
24    <body>
25        <pi><?test 123?><?test2 123?><?test2 1234?><?test 123?></pi>
26        <ps><p>text</p><p>text</p><p>other text</p></ps>
27        <psattrs><p align="center" xmlns:foo="some:bar">text</p><p xmlns:foo="some:bar">text</p><p align="center">text</p><p align="left" xmlns:foo="some:bar">text</p><p align="center" xmlns:foo="some:bar2">text</p><p align="center" xmlns:foo="some:bar">text</p></psattrs>
28        <comments><!-- comment 1 --><!-- comment 2 --><!-- comment 1 --></comments>
29        <texts>abc<i/>def<i/>abc</texts>
30        <cdatas><![CDATA[test]]> <![CDATA[test2]]> <![CDATA[test]]></cdatas>
31        <tree>
32            <div>
33                <p>A</p>
34                <div foo="bar">
35                    <p>B</p>
36                </div>
37            </div>
38            <p>A</p>
39        </tree>
40    </body>
41</html>
42XML);
43
44$xpath = new DOMXPath($dom1);
45
46function foreach_comparator($query) {
47    global $xpath;
48    $container = $xpath->query($query)[0];
49    $childNodes = iterator_to_array($container->childNodes);
50    $firstChild = $childNodes[0];
51    foreach ($childNodes as $child) {
52        var_dump($child->isEqualNode($firstChild));
53    }
54}
55
56function comparePairs($list1, $list2) {
57    $list1 = iterator_to_array($list1);
58    $list2 = iterator_to_array($list2);
59    usort($list1, function ($a, $b) {
60        return strcmp($a->nodeName, $b->nodeName);
61    });
62    usort($list2, function ($a, $b) {
63        return strcmp($a->nodeName, $b->nodeName);
64    });
65    foreach ($list1 as $entity1) {
66        foreach ($list2 as $entity2) {
67            echo "Comparing {$entity1->nodeName} with {$entity2->nodeName}\n";
68            var_dump($entity1->isEqualNode($entity2));
69        }
70    }
71}
72
73echo "--- Test edge cases ---\n";
74
75var_dump($dom1->doctype->isEqualNode(null));
76var_dump((new DOMDocument())->isEqualNode(new DOMDocument()));
77
78echo "--- Test doctype ---\n";
79
80var_dump($dom1->doctype->isEqualNode($dom1->doctype));
81$dom2->loadXML('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/x.dtd"><html/>');
82var_dump($dom1->doctype->isEqualNode($dom2->doctype));
83$dom2->loadXML('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN2" "http://www.w3.org/TR/html4/strict.dtd"><html/>');
84var_dump($dom1->doctype->isEqualNode($dom2->doctype));
85$dom2->loadXML('<!DOCTYPE HTML2 PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"><html/>');
86var_dump($dom1->doctype->isEqualNode($dom2->doctype));
87$dom2->loadXML('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"><html/>');
88var_dump($dom1->doctype->isEqualNode($dom2->doctype));
89
90echo "--- Test processing instruction ---\n";
91
92foreach_comparator("//pi");
93
94echo "--- Test comments ---\n";
95
96foreach_comparator("//comments");
97
98echo "--- Test texts ---\n";
99
100foreach_comparator("//texts");
101
102echo "--- Test CDATA ---\n";
103
104foreach_comparator("//cdatas");
105
106echo "--- Test attribute ---\n";
107
108var_dump((new DOMAttr("name", "value"))->isEqualNode(new DOMAttr("name", "value")));
109var_dump((new DOMAttr("name", "value"))->isEqualNode(new DOMAttr("name", "value2")));
110var_dump((new DOMAttr("name", "value"))->isEqualNode(new DOMAttr("name2", "value")));
111var_dump((new DOMAttr("name", "value"))->isEqualNode(new DOMAttr("name2", "value2")));
112var_dump((new DOMAttr("name", "value"))->isEqualNode(new DOMAttr("ns:name", "value")));
113
114echo "--- Test entity reference ---\n";
115
116var_dump((new DOMEntityReference("ref"))->isEqualNode(new DOMEntityReference("ref")));
117var_dump((new DOMEntityReference("ref"))->isEqualNode(new DOMEntityReference("ref2")));
118
119echo "--- Test entity declaration ---\n";
120
121$dom2->loadXML(<<<XML
122<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd" [
123    <!ENTITY barbar '<bar>bartext</bar>'>
124    <!ENTITY foo '<foo2/>'>
125    <!ENTITY bar '<bar>bartext</bar>'>
126]>
127<html/>
128XML);
129
130comparePairs($dom1->doctype->entities, $dom2->doctype->entities);
131
132echo "--- Test notation declaration ---\n";
133
134$dom2->loadXML(<<<XML
135<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd" [
136    <!NOTATION myNotation SYSTEM "test.dtd">
137    <!NOTATION myNotation2 SYSTEM "test2.dtd">
138    <!NOTATION myNotation3 SYSTEM "test.dtd">
139]>
140<html/>
141XML);
142
143comparePairs($dom1->doctype->notations, $dom2->doctype->notations);
144
145echo "--- Test element without attributes ---\n";
146
147foreach_comparator("//ps");
148
149echo "--- Test element with attributes ---\n";
150
151foreach_comparator("//psattrs");
152
153echo "--- Test element tree ---\n";
154
155$tree = $xpath->query("//tree")[0];
156$dom2->loadXML(<<<XML
157        <tree>
158            <div>
159                <p>A</p>
160                <div foo="bar">
161                    <p>B</p>
162                </div>
163            </div>
164            <p>A</p>
165        </tree>
166XML);
167var_dump($tree->isEqualNode($dom2->documentElement));
168$dom2->loadXML(<<<XML
169        <tree>
170            <p>A</p>
171            <div>
172                <p>A</p>
173                <div foo="bar">
174                    <p>B</p>
175                </div>
176            </div>
177        </tree>
178XML);
179var_dump($tree->isEqualNode($dom2->documentElement));
180$dom2->loadXML(<<<XML
181        <tree>
182            <div>
183                <p>A</p>
184                <div foo="bar">
185                </div>
186            </div>
187            <p>A</p>
188        </tree>
189XML);
190var_dump($tree->isEqualNode($dom2->documentElement));
191$dom2->loadXML(<<<XML
192        <tree>
193            <div>
194                <p>A</p>
195                <div foo="bar" extra="attr">
196                    <p>B</p>
197                </div>
198            </div>
199            <p>A</p>
200        </tree>
201XML);
202var_dump($tree->isEqualNode($dom2->documentElement));
203
204echo "--- Test documents ---\n";
205
206$dom1Clone = clone $dom1;
207var_dump($dom1->documentElement->isEqualNode($dom1Clone->documentElement));
208var_dump($dom1->documentElement->isEqualNode($dom2->documentElement));
209var_dump($dom1->isEqualNode($dom1Clone));
210var_dump($dom1->isEqualNode($dom2));
211var_dump($dom1->documentElement->isEqualNode($dom1Clone));
212var_dump($dom1->documentElement->isEqualNode($dom2));
213
214echo "--- Test document fragments ---\n";
215
216$fragment1 = $dom1->createDocumentFragment();
217$fragment1->appendChild($dom1->createElement('em'));
218$fragment2 = $dom1->createDocumentFragment();
219$fragment2->appendChild($dom1->createElement('em'));
220$fragment3 = $dom1->createDocumentFragment();
221$fragment3->appendChild($dom1->createElement('b'));
222$emptyFragment1 = $dom1->createDocumentFragment();
223$emptyFragment2 = $dom1->createDocumentFragment();
224
225var_dump($fragment1->isEqualNode($fragment2));
226var_dump($fragment1->isEqualNode($fragment3));
227var_dump($emptyFragment1->isEqualNode($fragment1));
228var_dump($emptyFragment1->isEqualNode($emptyFragment2));
229
230echo "--- Test document fragments with multiple child nodes ---\n";
231
232$fragment1 = $dom1->createDocumentFragment();
233$fragment1->appendChild($dom1->createElement('a'));
234$fragment1->appendChild($dom1->createElement('b'));
235$fragment1->appendChild($dom1->createElement('c'));
236$fragment2 = $dom2->createDocumentFragment();
237$fragment2->appendChild($dom2->createElement('a'));
238$fragment2->appendChild($dom2->createElement('b'));
239$fragment2->appendChild($dom2->createElement('c'));
240
241var_dump($fragment1->isEqualNode($fragment2));
242
243$fragment2->firstChild->nextSibling->nextSibling->remove();
244var_dump($fragment1->isEqualNode($fragment2));
245
246echo "--- Test x:includes ---\n";
247
248// Adapted from https://www.php.net/manual/en/domdocument.xinclude.php
249$dom = new DOMDocument();
250$dom->loadXML(<<<XML
251<?xml version="1.0" ?>
252<chapter xmlns:xi="http://www.w3.org/2001/XInclude">
253 <p>Hello</p>
254 <para>
255  <xi:include href="book.xml">
256   <xi:fallback>
257    <p>xinclude: book.xml not found</p>
258   </xi:fallback>
259  </xi:include>
260 </para>
261 <para><p>xinclude: book.xml not found</p></para>
262</chapter>
263XML);
264@$dom->xinclude();
265$xpath = new DOMXPath($dom);
266
267$firstPara = $dom->documentElement->firstElementChild->nextElementSibling;
268$secondPara = $dom->documentElement->firstElementChild->nextElementSibling->nextElementSibling;
269var_dump($firstPara->isEqualNode($secondPara));
270var_dump($firstPara->firstElementChild->isEqualNode($secondPara->firstElementChild));
271
272?>
273--EXPECT--
274--- Test edge cases ---
275bool(false)
276bool(true)
277--- Test doctype ---
278bool(true)
279bool(false)
280bool(false)
281bool(false)
282bool(true)
283--- Test processing instruction ---
284bool(true)
285bool(false)
286bool(false)
287bool(true)
288--- Test comments ---
289bool(true)
290bool(false)
291bool(true)
292--- Test texts ---
293bool(true)
294bool(false)
295bool(false)
296bool(false)
297bool(true)
298--- Test CDATA ---
299bool(true)
300bool(false)
301bool(false)
302bool(false)
303bool(true)
304--- Test attribute ---
305bool(true)
306bool(false)
307bool(false)
308bool(false)
309bool(false)
310--- Test entity reference ---
311bool(true)
312bool(false)
313--- Test entity declaration ---
314Comparing bar with bar
315bool(true)
316Comparing bar with barbar
317bool(false)
318Comparing bar with foo
319bool(false)
320Comparing foo with bar
321bool(false)
322Comparing foo with barbar
323bool(false)
324Comparing foo with foo
325bool(true)
326--- Test notation declaration ---
327Comparing myNotation with myNotation
328bool(true)
329Comparing myNotation with myNotation2
330bool(false)
331Comparing myNotation with myNotation3
332bool(false)
333--- Test element without attributes ---
334bool(true)
335bool(true)
336bool(false)
337--- Test element with attributes ---
338bool(true)
339bool(false)
340bool(false)
341bool(false)
342bool(false)
343bool(true)
344--- Test element tree ---
345bool(true)
346bool(false)
347bool(false)
348bool(false)
349--- Test documents ---
350bool(true)
351bool(false)
352bool(true)
353bool(false)
354bool(false)
355bool(false)
356--- Test document fragments ---
357bool(true)
358bool(false)
359bool(false)
360bool(true)
361--- Test document fragments with multiple child nodes ---
362bool(true)
363bool(false)
364--- Test x:includes ---
365bool(false)
366bool(true)
367