@@ -1814,4 +1814,220 @@ PHP_METHOD(Dom_Element, closest)
18141814	dom_element_closest (thisp , intern , return_value , selectors_str );
18151815}
18161816
1817+ zend_result  dom_modern_element_substituted_node_value_read (dom_object  * obj , zval  * retval )
1818+ {
1819+ 	DOM_PROP_NODE (xmlNodePtr , nodep , obj );
1820+ 
1821+ 	xmlChar  * content  =  xmlNodeGetContent (nodep );
1822+ 
1823+ 	if  (UNEXPECTED (content  ==  NULL )) {
1824+ 		php_dom_throw_error (INVALID_STATE_ERR , true);
1825+ 		return  FAILURE ;
1826+ 	} else  {
1827+ 		ZVAL_STRING (retval , (const  char  * ) content );
1828+ 		xmlFree (content );
1829+ 	}
1830+ 
1831+ 	return  SUCCESS ;
1832+ }
1833+ 
1834+ zend_result  dom_modern_element_substituted_node_value_write (dom_object  * obj , zval  * newval )
1835+ {
1836+ 	DOM_PROP_NODE (xmlNodePtr , nodep , obj );
1837+ 
1838+ 	php_libxml_invalidate_node_list_cache (obj -> document );
1839+ 	dom_remove_all_children (nodep );
1840+ 	xmlNodeSetContentLen (nodep , (xmlChar  * ) Z_STRVAL_P (newval ), Z_STRLEN_P (newval ));
1841+ 
1842+ 	return  SUCCESS ;
1843+ }
1844+ 
1845+ static  void  dom_element_get_in_scope_namespace_info (php_dom_libxml_ns_mapper  * ns_mapper , HashTable  * result , xmlNodePtr  nodep , dom_object  * intern )
1846+ {
1847+ 	HashTable  prefix_to_ns_table ;
1848+ 	zend_hash_init (& prefix_to_ns_table , 0 , NULL , NULL , false);
1849+ 	zend_hash_real_init_mixed (& prefix_to_ns_table );
1850+ 
1851+ 	/* https://www.w3.org/TR/1999/REC-xpath-19991116/#namespace-nodes */ 
1852+ 	for  (const  xmlNode  * cur  =  nodep ; cur  !=  NULL ; cur  =  cur -> parent ) {
1853+ 		if  (cur -> type  ==  XML_ELEMENT_NODE ) {
1854+ 			/* Find the last attribute */ 
1855+ 			const  xmlAttr  * last  =  NULL ;
1856+ 			for  (const  xmlAttr  * attr  =  cur -> properties ; attr  !=  NULL ; attr  =  attr -> next ) {
1857+ 				last  =  attr ;
1858+ 			}
1859+ 
1860+ 			/* Reversed loop because the parent traversal is reversed as well, 
1861+ 			 * this will keep the ordering consistent. */ 
1862+ 			for  (const  xmlAttr  * attr  =  last ; attr  !=  NULL ; attr  =  attr -> prev ) {
1863+ 				if  (attr -> ns  !=  NULL  &&  php_dom_ns_is_fast_ex (attr -> ns , php_dom_ns_is_xmlns_magic_token )
1864+ 					&&  attr -> children  !=  NULL  &&  attr -> children -> content  !=  NULL ) {
1865+ 					const  char  * prefix  =  attr -> ns -> prefix  ==  NULL  ? NULL  : (const  char  * ) attr -> name ;
1866+ 					const  char  * key  =  prefix  ==  NULL  ? ""  : prefix ;
1867+ 					xmlNsPtr  ns  =  php_dom_libxml_ns_mapper_get_ns_raw_strings_nullsafe (ns_mapper , prefix , (const  char  * ) attr -> children -> content );
1868+ 					zend_hash_str_add_ptr (& prefix_to_ns_table , key , strlen (key ), ns );
1869+ 				}
1870+ 			}
1871+ 		}
1872+ 	}
1873+ 
1874+ 	xmlNsPtr  ns ;
1875+ 	zend_string  * prefix ;
1876+ 	ZEND_HASH_MAP_REVERSE_FOREACH_STR_KEY_PTR (& prefix_to_ns_table , prefix , ns ) {
1877+ 		if  (ZSTR_LEN (prefix ) ==  0  &&  (ns  ==  NULL  ||  ns -> href  ==  NULL  ||  * ns -> href  ==  '\0' )) {
1878+ 			/* Exception: "the value of the xmlns attribute for the nearest such element is non-empty" */ 
1879+ 			continue ;
1880+ 		}
1881+ 
1882+ 		zval  zv ;
1883+ 		object_init_ex (& zv , dom_namespace_info_class_entry );
1884+ 		zend_object  * obj  =  Z_OBJ (zv );
1885+ 
1886+ 		if  (ZSTR_LEN (prefix ) !=  0 ) {
1887+ 			ZVAL_STR_COPY (OBJ_PROP_NUM (obj , 0 ), prefix );
1888+ 		} else  {
1889+ 			ZVAL_NULL (OBJ_PROP_NUM (obj , 0 ));
1890+ 		}
1891+ 
1892+ 		if  (ns  !=  NULL  &&  ns -> href  !=  NULL  &&  * ns -> href  !=  '\0' ) {
1893+ 			ZVAL_STRING (OBJ_PROP_NUM (obj , 1 ), (const  char  * ) ns -> href );
1894+ 		} else  {
1895+ 			ZVAL_NULL (OBJ_PROP_NUM (obj , 1 ));
1896+ 		}
1897+ 
1898+ 		php_dom_create_object (nodep , OBJ_PROP_NUM (obj , 2 ), intern );
1899+ 
1900+ 		zend_hash_next_index_insert_new (result , & zv );
1901+ 	} ZEND_HASH_FOREACH_END ();
1902+ 
1903+ 	zend_hash_destroy (& prefix_to_ns_table );
1904+ }
1905+ 
1906+ PHP_METHOD (Dom_Element , getInScopeNamespaces )
1907+ {
1908+ 	zval  * id ;
1909+ 	xmlNode  * nodep ;
1910+ 	dom_object  * intern ;
1911+ 
1912+ 	if  (zend_parse_parameters_none () !=  SUCCESS ) {
1913+ 		RETURN_THROWS ();
1914+ 	}
1915+ 
1916+ 	DOM_GET_THIS_OBJ (nodep , id , xmlNodePtr , intern );
1917+ 
1918+ 	php_dom_libxml_ns_mapper  * ns_mapper  =  php_dom_get_ns_mapper (intern );
1919+ 
1920+ 	array_init (return_value );
1921+ 	HashTable  * result  =  Z_ARRVAL_P (return_value );
1922+ 
1923+ 	dom_element_get_in_scope_namespace_info (ns_mapper , result , nodep , intern );
1924+ }
1925+ 
1926+ PHP_METHOD (Dom_Element , getDescendantNamespaces )
1927+ {
1928+ 	zval  * id ;
1929+ 	xmlNode  * nodep ;
1930+ 	dom_object  * intern ;
1931+ 
1932+ 	if  (zend_parse_parameters_none () !=  SUCCESS ) {
1933+ 		RETURN_THROWS ();
1934+ 	}
1935+ 
1936+ 	DOM_GET_THIS_OBJ (nodep , id , xmlNodePtr , intern );
1937+ 
1938+ 	php_dom_libxml_ns_mapper  * ns_mapper  =  php_dom_get_ns_mapper (intern );
1939+ 
1940+ 	array_init (return_value );
1941+ 	HashTable  * result  =  Z_ARRVAL_P (return_value );
1942+ 
1943+ 	dom_element_get_in_scope_namespace_info (ns_mapper , result , nodep , intern );
1944+ 
1945+ 	xmlNodePtr  cur  =  nodep -> children ;
1946+ 	while  (cur  !=  NULL ) {
1947+ 		if  (cur -> type  ==  XML_ELEMENT_NODE ) {
1948+ 			/* TODO: this could be more optimized by updating the same HashTable repeatedly 
1949+ 			 * instead of recreating it on every node. */ 
1950+ 			dom_element_get_in_scope_namespace_info (ns_mapper , result , cur , intern );
1951+ 
1952+ 			if  (cur -> children ) {
1953+ 				cur  =  cur -> children ;
1954+ 				continue ;
1955+ 			}
1956+ 		}
1957+ 
1958+ 		cur  =  php_dom_next_in_tree_order (cur , nodep );
1959+ 	}
1960+ }
1961+ 
1962+ PHP_METHOD (Dom_Element , rename )
1963+ {
1964+ 	zend_string  * namespace_uri , * qualified_name ;
1965+ 	ZEND_PARSE_PARAMETERS_START (2 , 2 )
1966+ 		Z_PARAM_STR_OR_NULL (namespace_uri )
1967+ 		Z_PARAM_STR (qualified_name )
1968+ 	ZEND_PARSE_PARAMETERS_END ();
1969+ 
1970+ 	zval  * id ;
1971+ 	dom_object  * intern ;
1972+ 	xmlNodePtr  nodep ;
1973+ 	DOM_GET_THIS_OBJ (nodep , id , xmlNodePtr , intern );
1974+ 
1975+ 	xmlChar  * localname  =  NULL , * prefix  =  NULL ;
1976+ 	int  errorcode  =  dom_validate_and_extract (namespace_uri , qualified_name , & localname , & prefix );
1977+ 	if  (UNEXPECTED (errorcode  !=  0 )) {
1978+ 		php_dom_throw_error (errorcode , /* strict */  true);
1979+ 		goto cleanup ;
1980+ 	}
1981+ 
1982+ 	if  (nodep -> type  ==  XML_ATTRIBUTE_NODE ) {
1983+ 		/* Check for duplicate attributes. */ 
1984+ 		xmlAttrPtr  existing  =  xmlHasNsProp (nodep -> parent , localname , namespace_uri  &&  ZSTR_VAL (namespace_uri )[0 ] !=  '\0'  ? BAD_CAST  ZSTR_VAL (namespace_uri ) : NULL );
1985+ 		if  (existing  !=  NULL  &&  existing  !=  (xmlAttrPtr ) nodep ) {
1986+ 			php_dom_throw_error_with_message (INVALID_MODIFICATION_ERR , "An attribute with the given name in the given namespace already exists" , /* strict */  true);
1987+ 			goto cleanup ;
1988+ 		}
1989+ 	} else  {
1990+ 		ZEND_ASSERT (nodep -> type  ==  XML_ELEMENT_NODE );
1991+ 
1992+ 		/* Check for moving to or away from the HTML namespace. */ 
1993+ 		bool  is_currently_html_ns  =  php_dom_ns_is_fast (nodep , php_dom_ns_is_html_magic_token );
1994+ 		bool  will_be_html_ns  =  namespace_uri  !=  NULL  &&  zend_string_equals_literal (namespace_uri , DOM_XHTML_NS_URI );
1995+ 		if  (is_currently_html_ns  !=  will_be_html_ns ) {
1996+ 			if  (is_currently_html_ns ) {
1997+ 				php_dom_throw_error_with_message (
1998+ 					INVALID_MODIFICATION_ERR ,
1999+ 					"It is not possible to move an element out of the HTML namespace because the HTML namespace is tied to the HTMLElement class" ,
2000+ 					/* strict */  true
2001+ 				);
2002+ 			} else  {
2003+ 				php_dom_throw_error_with_message (
2004+ 					INVALID_MODIFICATION_ERR ,
2005+ 					"It is not possible to move an element into the HTML namespace because the HTML namespace is tied to the HTMLElement class" ,
2006+ 					/* strict */  true
2007+ 				);
2008+ 			}
2009+ 			goto cleanup ;
2010+ 		}
2011+ 	}
2012+ 
2013+ 	php_libxml_invalidate_node_list_cache (intern -> document );
2014+ 
2015+ 	php_dom_libxml_ns_mapper  * ns_mapper  =  php_dom_get_ns_mapper (intern );
2016+ 
2017+ 	/* Update namespace uri + prefix by querying the namespace mapper */ 
2018+ 	/* prefix can be NULL here, but that is taken care of by the called APIs. */ 
2019+ 	nodep -> ns  =  php_dom_libxml_ns_mapper_get_ns_raw_prefix_string (ns_mapper , prefix , xmlStrlen (prefix ), namespace_uri );
2020+ 
2021+ 	/* Change the local name */ 
2022+ 	if  (xmlDictOwns (nodep -> doc -> dict , nodep -> name ) !=  1 ) {
2023+ 		xmlFree ((xmlChar  * ) nodep -> name );
2024+ 	}
2025+ 	nodep -> name  =  localname ;
2026+ 	localname  =  NULL ;
2027+ 
2028+ cleanup :
2029+ 	xmlFree (localname );
2030+ 	xmlFree (prefix );
2031+ }
2032+ 
18172033#endif 
0 commit comments