@@ -1425,6 +1425,155 @@ PHP_METHOD(DOMNode, isSameNode)
1425
1425
}
1426
1426
/* }}} end dom_node_is_same_node */
1427
1427
1428
+ static bool php_dom_node_is_content_equal (const xmlNode * this , const xmlNode * other )
1429
+ {
1430
+ xmlChar * this_content = xmlNodeGetContent (this );
1431
+ xmlChar * other_content = xmlNodeGetContent (other );
1432
+ bool result = xmlStrEqual (this_content , other_content );
1433
+ xmlFree (this_content );
1434
+ xmlFree (other_content );
1435
+ return result ;
1436
+ }
1437
+
1438
+ static bool php_dom_node_is_ns_uri_equal (const xmlNode * this , const xmlNode * other )
1439
+ {
1440
+ const xmlChar * this_ns = this -> ns ? this -> ns -> href : NULL ;
1441
+ const xmlChar * other_ns = other -> ns ? other -> ns -> href : NULL ;
1442
+ return xmlStrEqual (this_ns , other_ns );
1443
+ }
1444
+
1445
+ static bool php_dom_node_is_ns_prefix_equal (const xmlNode * this , const xmlNode * other )
1446
+ {
1447
+ const xmlChar * this_ns = this -> ns ? this -> ns -> prefix : NULL ;
1448
+ const xmlChar * other_ns = other -> ns ? other -> ns -> prefix : NULL ;
1449
+ return xmlStrEqual (this_ns , other_ns );
1450
+ }
1451
+
1452
+ static bool php_dom_node_is_equal_node (const xmlNode * this , const xmlNode * other );
1453
+
1454
+ #define PHP_DOM_FUNC_CAT (prefix , suffix ) prefix##_##suffix
1455
+ /* xmlNode and xmlNs have incompatible struct layouts, i.e. the next field is in a different offset */
1456
+ #define PHP_DOM_DEFINE_LIST_EQUALITY_HELPER (type ) \
1457
+ static size_t PHP_DOM_FUNC_CAT(php_dom_node_count_list_size, type)(const type *node) \
1458
+ { \
1459
+ size_t counter = 0; \
1460
+ while (node) { \
1461
+ counter++; \
1462
+ node = node->next; \
1463
+ } \
1464
+ return counter; \
1465
+ } \
1466
+ static bool PHP_DOM_FUNC_CAT(php_dom_node_list_equality_check, type)(const type *list1, const type *list2) \
1467
+ { \
1468
+ size_t count = PHP_DOM_FUNC_CAT(php_dom_node_count_list_size, type)(list1); \
1469
+ if (count != PHP_DOM_FUNC_CAT(php_dom_node_count_list_size, type)(list2)) { \
1470
+ return false; \
1471
+ } \
1472
+ for (size_t i = 0; i < count; i++) { \
1473
+ if (!php_dom_node_is_equal_node((const xmlNode *) list1, (const xmlNode *) list2)) { \
1474
+ return false; \
1475
+ } \
1476
+ list1 = list1->next; \
1477
+ list2 = list2->next; \
1478
+ } \
1479
+ return true; \
1480
+ }
1481
+ PHP_DOM_DEFINE_LIST_EQUALITY_HELPER (xmlNode )
1482
+ PHP_DOM_DEFINE_LIST_EQUALITY_HELPER (xmlNs )
1483
+
1484
+ static bool php_dom_node_is_equal_node (const xmlNode * this , const xmlNode * other )
1485
+ {
1486
+ ZEND_ASSERT (this != NULL );
1487
+ ZEND_ASSERT (other != NULL );
1488
+
1489
+ if (this -> type != other -> type ) {
1490
+ return false;
1491
+ }
1492
+
1493
+ /* Notes:
1494
+ * - XML_DOCUMENT_TYPE_NODE is no longer created by libxml2, we only have to support XML_DTD_NODE.
1495
+ * - element and attribute declarations are not exposed as nodes in DOM, so no comparison is needed for those. */
1496
+ if (this -> type == XML_ELEMENT_NODE ) {
1497
+ return xmlStrEqual (this -> name , other -> name )
1498
+ && php_dom_node_is_ns_prefix_equal (this , other )
1499
+ && php_dom_node_is_ns_uri_equal (this , other )
1500
+ /* Check attributes first, then namespace declarations, then children */
1501
+ && php_dom_node_list_equality_check_xmlNode ((const xmlNode * ) this -> properties , (const xmlNode * ) other -> properties )
1502
+ && php_dom_node_list_equality_check_xmlNs (this -> nsDef , other -> nsDef )
1503
+ && php_dom_node_list_equality_check_xmlNode (this -> children , other -> children );
1504
+ } else if (this -> type == XML_DTD_NODE ) {
1505
+ /* Note: in the living spec entity declarations and notations are no longer compared because they're considered obsolete. */
1506
+ const xmlDtd * this_dtd = (const xmlDtd * ) this ;
1507
+ const xmlDtd * other_dtd = (const xmlDtd * ) other ;
1508
+ return xmlStrEqual (this_dtd -> name , other_dtd -> name )
1509
+ && xmlStrEqual (this_dtd -> ExternalID , other_dtd -> ExternalID )
1510
+ && xmlStrEqual (this_dtd -> SystemID , other_dtd -> SystemID );
1511
+ } else if (this -> type == XML_PI_NODE ) {
1512
+ return xmlStrEqual (this -> name , other -> name ) && xmlStrEqual (this -> content , other -> content );
1513
+ } else if (this -> type == XML_TEXT_NODE || this -> type == XML_COMMENT_NODE || this -> type == XML_CDATA_SECTION_NODE ) {
1514
+ return xmlStrEqual (this -> content , other -> content );
1515
+ } else if (this -> type == XML_ATTRIBUTE_NODE ) {
1516
+ const xmlAttr * this_attr = (const xmlAttr * ) this ;
1517
+ const xmlAttr * other_attr = (const xmlAttr * ) other ;
1518
+ return xmlStrEqual (this_attr -> name , other_attr -> name )
1519
+ && php_dom_node_is_ns_uri_equal (this , other )
1520
+ && php_dom_node_is_content_equal (this , other );
1521
+ } else if (this -> type == XML_ENTITY_REF_NODE ) {
1522
+ return xmlStrEqual (this -> name , other -> name );
1523
+ } else if (this -> type == XML_ENTITY_DECL || this -> type == XML_NOTATION_NODE || this -> type == XML_ENTITY_NODE ) {
1524
+ const xmlEntity * this_entity = (const xmlEntity * ) this ;
1525
+ const xmlEntity * other_entity = (const xmlEntity * ) other ;
1526
+ return this_entity -> etype == other_entity -> etype
1527
+ && xmlStrEqual (this_entity -> name , other_entity -> name )
1528
+ && xmlStrEqual (this_entity -> ExternalID , other_entity -> ExternalID )
1529
+ && xmlStrEqual (this_entity -> SystemID , other_entity -> SystemID )
1530
+ && php_dom_node_is_content_equal (this , other );
1531
+ } else if (this -> type == XML_NAMESPACE_DECL ) {
1532
+ const xmlNs * this_ns = (const xmlNs * ) this ;
1533
+ const xmlNs * other_ns = (const xmlNs * ) other ;
1534
+ return xmlStrEqual (this_ns -> prefix , other_ns -> prefix ) && xmlStrEqual (this_ns -> href , other_ns -> href );
1535
+ } else if (this -> type == XML_DOCUMENT_FRAG_NODE || this -> type == XML_HTML_DOCUMENT_NODE || this -> type == XML_DOCUMENT_NODE ) {
1536
+ return php_dom_node_list_equality_check_xmlNode (this -> children , other -> children );
1537
+ }
1538
+
1539
+ return false;
1540
+ }
1541
+
1542
+ /* {{{ URL: https://dom.spec.whatwg.org/#dom-node-isequalnode (for everything still in the living spec)
1543
+ * 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)
1544
+ Since: DOM Level 3
1545
+ */
1546
+ PHP_METHOD (DOMNode , isEqualNode )
1547
+ {
1548
+ zval * id , * node ;
1549
+ xmlNodePtr otherp , nodep ;
1550
+ dom_object * unused_intern ;
1551
+
1552
+ id = ZEND_THIS ;
1553
+ if (zend_parse_parameters (ZEND_NUM_ARGS (), "O!" , & node , dom_node_class_entry ) == FAILURE ) {
1554
+ RETURN_THROWS ();
1555
+ }
1556
+
1557
+ if (node == NULL ) {
1558
+ RETURN_FALSE ;
1559
+ }
1560
+
1561
+ DOM_GET_THIS_OBJ (nodep , id , xmlNodePtr , unused_intern );
1562
+ DOM_GET_OBJ (otherp , node , xmlNodePtr , unused_intern );
1563
+
1564
+ if (nodep == otherp ) {
1565
+ RETURN_TRUE ;
1566
+ }
1567
+
1568
+ /* Empty fragments/documents only match if they're both empty */
1569
+ if (UNEXPECTED (nodep == NULL || otherp == NULL )) {
1570
+ RETURN_BOOL (nodep == NULL && otherp == NULL );
1571
+ }
1572
+
1573
+ RETURN_BOOL (php_dom_node_is_equal_node (nodep , otherp ));
1574
+ }
1575
+ /* }}} end DOMNode::isEqualNode */
1576
+
1428
1577
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Node3-lookupNamespacePrefix
1429
1578
Since: DOM Level 3
1430
1579
*/
0 commit comments