@@ -1814,4 +1814,220 @@ PHP_METHOD(Dom_Element, closest)
1814
1814
dom_element_closest (thisp , intern , return_value , selectors_str );
1815
1815
}
1816
1816
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
+
1817
2033
#endif
0 commit comments