@@ -1471,6 +1471,155 @@ PHP_METHOD(DOMNode, isSameNode)
1471
1471
}
1472
1472
/* }}} end dom_node_is_same_node */
1473
1473
1474
+ static bool php_dom_node_is_content_equal (const xmlNode * this , const xmlNode * other )
1475
+ {
1476
+ xmlChar * this_content = xmlNodeGetContent (this );
1477
+ xmlChar * other_content = xmlNodeGetContent (other );
1478
+ bool result = xmlStrEqual (this_content , other_content );
1479
+ xmlFree (this_content );
1480
+ xmlFree (other_content );
1481
+ return result ;
1482
+ }
1483
+
1484
+ static bool php_dom_node_is_ns_uri_equal (const xmlNode * this , const xmlNode * other )
1485
+ {
1486
+ const xmlChar * this_ns = this -> ns ? this -> ns -> href : NULL ;
1487
+ const xmlChar * other_ns = other -> ns ? other -> ns -> href : NULL ;
1488
+ return xmlStrEqual (this_ns , other_ns );
1489
+ }
1490
+
1491
+ static bool php_dom_node_is_ns_prefix_equal (const xmlNode * this , const xmlNode * other )
1492
+ {
1493
+ const xmlChar * this_ns = this -> ns ? this -> ns -> prefix : NULL ;
1494
+ const xmlChar * other_ns = other -> ns ? other -> ns -> prefix : NULL ;
1495
+ return xmlStrEqual (this_ns , other_ns );
1496
+ }
1497
+
1498
+ static bool php_dom_node_is_equal_node (const xmlNode * this , const xmlNode * other );
1499
+
1500
+ #define PHP_DOM_FUNC_CAT (prefix , suffix ) prefix##_##suffix
1501
+ /* xmlNode and xmlNs have incompatible struct layouts, i.e. the next field is in a different offset */
1502
+ #define PHP_DOM_DEFINE_LIST_EQUALITY_HELPER (type ) \
1503
+ static size_t PHP_DOM_FUNC_CAT(php_dom_node_count_list_size, type)(const type *node) \
1504
+ { \
1505
+ size_t counter = 0; \
1506
+ while (node) { \
1507
+ counter++; \
1508
+ node = node->next; \
1509
+ } \
1510
+ return counter; \
1511
+ } \
1512
+ static bool PHP_DOM_FUNC_CAT(php_dom_node_list_equality_check, type)(const type *list1, const type *list2) \
1513
+ { \
1514
+ size_t count = PHP_DOM_FUNC_CAT(php_dom_node_count_list_size, type)(list1); \
1515
+ if (count != PHP_DOM_FUNC_CAT(php_dom_node_count_list_size, type)(list2)) { \
1516
+ return false; \
1517
+ } \
1518
+ for (size_t i = 0; i < count; i++) { \
1519
+ if (!php_dom_node_is_equal_node((const xmlNode *) list1, (const xmlNode *) list2)) { \
1520
+ return false; \
1521
+ } \
1522
+ list1 = list1->next; \
1523
+ list2 = list2->next; \
1524
+ } \
1525
+ return true; \
1526
+ }
1527
+ PHP_DOM_DEFINE_LIST_EQUALITY_HELPER (xmlNode )
1528
+ PHP_DOM_DEFINE_LIST_EQUALITY_HELPER (xmlNs )
1529
+
1530
+ static bool php_dom_node_is_equal_node (const xmlNode * this , const xmlNode * other )
1531
+ {
1532
+ ZEND_ASSERT (this != NULL );
1533
+ ZEND_ASSERT (other != NULL );
1534
+
1535
+ if (this -> type != other -> type ) {
1536
+ return false;
1537
+ }
1538
+
1539
+ /* Notes:
1540
+ * - XML_DOCUMENT_TYPE_NODE is no longer created by libxml2, we only have to support XML_DTD_NODE.
1541
+ * - element and attribute declarations are not exposed as nodes in DOM, so no comparison is needed for those. */
1542
+ if (this -> type == XML_ELEMENT_NODE ) {
1543
+ return xmlStrEqual (this -> name , other -> name )
1544
+ && php_dom_node_is_ns_prefix_equal (this , other )
1545
+ && php_dom_node_is_ns_uri_equal (this , other )
1546
+ /* Check attributes first, then namespace declarations, then children */
1547
+ && php_dom_node_list_equality_check_xmlNode ((const xmlNode * ) this -> properties , (const xmlNode * ) other -> properties )
1548
+ && php_dom_node_list_equality_check_xmlNs (this -> nsDef , other -> nsDef )
1549
+ && php_dom_node_list_equality_check_xmlNode (this -> children , other -> children );
1550
+ } else if (this -> type == XML_DTD_NODE ) {
1551
+ /* Note: in the living spec entity declarations and notations are no longer compared because they're considered obsolete. */
1552
+ const xmlDtd * this_dtd = (const xmlDtd * ) this ;
1553
+ const xmlDtd * other_dtd = (const xmlDtd * ) other ;
1554
+ return xmlStrEqual (this_dtd -> name , other_dtd -> name )
1555
+ && xmlStrEqual (this_dtd -> ExternalID , other_dtd -> ExternalID )
1556
+ && xmlStrEqual (this_dtd -> SystemID , other_dtd -> SystemID );
1557
+ } else if (this -> type == XML_PI_NODE ) {
1558
+ return xmlStrEqual (this -> name , other -> name ) && xmlStrEqual (this -> content , other -> content );
1559
+ } else if (this -> type == XML_TEXT_NODE || this -> type == XML_COMMENT_NODE || this -> type == XML_CDATA_SECTION_NODE ) {
1560
+ return xmlStrEqual (this -> content , other -> content );
1561
+ } else if (this -> type == XML_ATTRIBUTE_NODE ) {
1562
+ const xmlAttr * this_attr = (const xmlAttr * ) this ;
1563
+ const xmlAttr * other_attr = (const xmlAttr * ) other ;
1564
+ return xmlStrEqual (this_attr -> name , other_attr -> name )
1565
+ && php_dom_node_is_ns_uri_equal (this , other )
1566
+ && php_dom_node_is_content_equal (this , other );
1567
+ } else if (this -> type == XML_ENTITY_REF_NODE ) {
1568
+ return xmlStrEqual (this -> name , other -> name );
1569
+ } else if (this -> type == XML_ENTITY_DECL || this -> type == XML_NOTATION_NODE || this -> type == XML_ENTITY_NODE ) {
1570
+ const xmlEntity * this_entity = (const xmlEntity * ) this ;
1571
+ const xmlEntity * other_entity = (const xmlEntity * ) other ;
1572
+ return this_entity -> etype == other_entity -> etype
1573
+ && xmlStrEqual (this_entity -> name , other_entity -> name )
1574
+ && xmlStrEqual (this_entity -> ExternalID , other_entity -> ExternalID )
1575
+ && xmlStrEqual (this_entity -> SystemID , other_entity -> SystemID )
1576
+ && php_dom_node_is_content_equal (this , other );
1577
+ } else if (this -> type == XML_NAMESPACE_DECL ) {
1578
+ const xmlNs * this_ns = (const xmlNs * ) this ;
1579
+ const xmlNs * other_ns = (const xmlNs * ) other ;
1580
+ return xmlStrEqual (this_ns -> prefix , other_ns -> prefix ) && xmlStrEqual (this_ns -> href , other_ns -> href );
1581
+ } else if (this -> type == XML_DOCUMENT_FRAG_NODE || this -> type == XML_HTML_DOCUMENT_NODE || this -> type == XML_DOCUMENT_NODE ) {
1582
+ return php_dom_node_list_equality_check_xmlNode (this -> children , other -> children );
1583
+ }
1584
+
1585
+ return false;
1586
+ }
1587
+
1588
+ /* {{{ URL: https://dom.spec.whatwg.org/#dom-node-isequalnode (for everything still in the living spec)
1589
+ * URL: https://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/DOM3-Core.html#core-Node3-isEqualNode (for old nodes removed from the living spec)
1590
+ Since: DOM Level 3
1591
+ */
1592
+ PHP_METHOD (DOMNode , isEqualNode )
1593
+ {
1594
+ zval * id , * node ;
1595
+ xmlNodePtr otherp , nodep ;
1596
+ dom_object * unused_intern ;
1597
+
1598
+ id = ZEND_THIS ;
1599
+ if (zend_parse_parameters (ZEND_NUM_ARGS (), "O!" , & node , dom_node_class_entry ) == FAILURE ) {
1600
+ RETURN_THROWS ();
1601
+ }
1602
+
1603
+ if (node == NULL ) {
1604
+ RETURN_FALSE ;
1605
+ }
1606
+
1607
+ DOM_GET_THIS_OBJ (nodep , id , xmlNodePtr , unused_intern );
1608
+ DOM_GET_OBJ (otherp , node , xmlNodePtr , unused_intern );
1609
+
1610
+ if (nodep == otherp ) {
1611
+ RETURN_TRUE ;
1612
+ }
1613
+
1614
+ /* Empty fragments/documents only match if they're both empty */
1615
+ if (UNEXPECTED (nodep == NULL || otherp == NULL )) {
1616
+ RETURN_BOOL (nodep == NULL && otherp == NULL );
1617
+ }
1618
+
1619
+ RETURN_BOOL (php_dom_node_is_equal_node (nodep , otherp ));
1620
+ }
1621
+ /* }}} end DOMNode::isEqualNode */
1622
+
1474
1623
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Node3-lookupNamespacePrefix
1475
1624
Since: DOM Level 3
1476
1625
*/
0 commit comments