@@ -1742,6 +1742,59 @@ zend_result dom_modern_element_substituted_node_value_write(dom_object *obj, zva
17421742 return SUCCESS ;
17431743}
17441744
1745+ static void dom_element_get_in_scope_namespace_info (php_dom_libxml_ns_mapper * ns_mapper , HashTable * result , xmlNodePtr nodep , dom_object * intern )
1746+ {
1747+ HashTable prefix_to_ns_table ;
1748+ zend_hash_init (& prefix_to_ns_table , 0 , NULL , NULL , false);
1749+ zend_hash_real_init_mixed (& prefix_to_ns_table );
1750+
1751+ /* https://www.w3.org/TR/1999/REC-xpath-19991116/#namespace-nodes */
1752+ for (const xmlNode * cur = nodep ; cur != NULL ; cur = cur -> parent ) {
1753+ if (cur -> type == XML_ELEMENT_NODE ) {
1754+ for (const xmlAttr * attr = cur -> properties ; attr != NULL ; attr = attr -> next ) {
1755+ if (attr -> ns != NULL && php_dom_ns_is_fast_ex (attr -> ns , php_dom_ns_is_xmlns_magic_token )
1756+ && attr -> children != NULL && attr -> children -> content != NULL ) {
1757+ const char * prefix = attr -> ns -> prefix == NULL ? NULL : (const char * ) attr -> name ;
1758+ const char * key = prefix == NULL ? "" : prefix ;
1759+ xmlNsPtr ns = php_dom_libxml_ns_mapper_get_ns_raw_strings_nullsafe (ns_mapper , prefix , (const char * ) attr -> children -> content );
1760+ zend_hash_str_add_ptr (& prefix_to_ns_table , key , strlen (key ), ns );
1761+ }
1762+ }
1763+ }
1764+ }
1765+
1766+ xmlNsPtr ns ;
1767+ zend_string * prefix ;
1768+ ZEND_HASH_MAP_FOREACH_STR_KEY_PTR (& prefix_to_ns_table , prefix , ns ) {
1769+ if (ZSTR_LEN (prefix ) == 0 && (ns == NULL || ns -> href == NULL || * ns -> href == '\0' )) {
1770+ /* Exception: "the value of the xmlns attribute for the nearest such element is non-empty" */
1771+ continue ;
1772+ }
1773+
1774+ zval zv ;
1775+ object_init_ex (& zv , dom_namespace_info_class_entry );
1776+ zend_object * obj = Z_OBJ (zv );
1777+
1778+ if (ZSTR_LEN (prefix ) != 0 ) {
1779+ ZVAL_STR_COPY (OBJ_PROP_NUM (obj , 0 ), prefix );
1780+ } else {
1781+ ZVAL_NULL (OBJ_PROP_NUM (obj , 0 ));
1782+ }
1783+
1784+ if (ns != NULL && ns -> href != NULL && * ns -> href != '\0' ) {
1785+ ZVAL_STRING (OBJ_PROP_NUM (obj , 1 ), (const char * ) ns -> href );
1786+ } else {
1787+ ZVAL_NULL (OBJ_PROP_NUM (obj , 1 ));
1788+ }
1789+
1790+ php_dom_create_object (nodep , OBJ_PROP_NUM (obj , 2 ), intern );
1791+
1792+ zend_hash_next_index_insert_new (result , & zv );
1793+ } ZEND_HASH_FOREACH_END ();
1794+
1795+ zend_hash_destroy (& prefix_to_ns_table );
1796+ }
1797+
17451798PHP_METHOD (DOM_Element , getInScopeNamespaces )
17461799{
17471800 zval * id ;
@@ -1755,22 +1808,47 @@ PHP_METHOD(DOM_Element, getInScopeNamespaces)
17551808 DOM_GET_THIS_OBJ (nodep , id , xmlNodePtr , intern );
17561809
17571810 php_dom_libxml_ns_mapper * ns_mapper = php_dom_get_ns_mapper (intern );
1758- php_dom_in_scope_ns namespaces = php_dom_get_in_scope_ns (ns_mapper , nodep , true);
17591811
1760- array_init_size (return_value , namespaces .count );
1812+ array_init (return_value );
1813+ HashTable * result = Z_ARRVAL_P (return_value );
17611814
1762- for (size_t i = 0 ; i < namespaces .count ; i ++ ) {
1763- xmlNsPtr ns = namespaces .list [i ];
1764- if (ns == NULL ) {
1765- /* This case corresponds to xmlns="" */
1766- add_assoc_null (return_value , "" );
1767- } else {
1768- const char * prefix = (const char * ) ns -> prefix ;
1769- add_assoc_stringl (return_value , prefix == NULL ? "" : prefix , (const char * ) ns -> href , xmlStrlen (ns -> href ));
1770- }
1815+ dom_element_get_in_scope_namespace_info (ns_mapper , result , nodep , intern );
1816+ }
1817+
1818+ PHP_METHOD (DOM_Element , getDescendantNamespaces )
1819+ {
1820+ zval * id ;
1821+ xmlNode * nodep ;
1822+ dom_object * intern ;
1823+
1824+ if (zend_parse_parameters_none () != SUCCESS ) {
1825+ RETURN_THROWS ();
17711826 }
17721827
1773- php_dom_in_scope_ns_destroy (& namespaces );
1828+ DOM_GET_THIS_OBJ (nodep , id , xmlNodePtr , intern );
1829+
1830+ php_dom_libxml_ns_mapper * ns_mapper = php_dom_get_ns_mapper (intern );
1831+
1832+ array_init (return_value );
1833+ HashTable * result = Z_ARRVAL_P (return_value );
1834+
1835+ dom_element_get_in_scope_namespace_info (ns_mapper , result , nodep , intern );
1836+
1837+ xmlNodePtr cur = nodep -> children ;
1838+ while (cur != NULL ) {
1839+ if (cur -> type == XML_ELEMENT_NODE ) {
1840+ /* TODO: this could be more optimized by updating the same HashTable repeatedly
1841+ * instead of recreating it on every node. */
1842+ dom_element_get_in_scope_namespace_info (ns_mapper , result , cur , intern );
1843+
1844+ if (cur -> children ) {
1845+ cur = cur -> children ;
1846+ continue ;
1847+ }
1848+ }
1849+
1850+ cur = php_dom_next_in_tree_order (cur , nodep );
1851+ }
17741852}
17751853
17761854PHP_METHOD (DOM_Element , rename )
0 commit comments