@@ -1814,4 +1814,219 @@ 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
+ /* NULL is a valid value for the sentinel */
1869
+ zval zv ;
1870
+ ZVAL_PTR (& zv , ns );
1871
+ zend_hash_str_add (& prefix_to_ns_table , key , strlen (key ), & zv );
1872
+ }
1873
+ }
1874
+ }
1875
+ }
1876
+
1877
+ xmlNsPtr ns ;
1878
+ zend_string * prefix ;
1879
+ ZEND_HASH_MAP_REVERSE_FOREACH_STR_KEY_PTR (& prefix_to_ns_table , prefix , ns ) {
1880
+ if (ZSTR_LEN (prefix ) == 0 && (ns == NULL || ns -> href == NULL || * ns -> href == '\0' )) {
1881
+ /* Exception: "the value of the xmlns attribute for the nearest such element is non-empty" */
1882
+ continue ;
1883
+ }
1884
+
1885
+ zval zv ;
1886
+ object_init_ex (& zv , dom_namespace_info_class_entry );
1887
+ zend_object * obj = Z_OBJ (zv );
1888
+
1889
+ if (ZSTR_LEN (prefix ) != 0 ) {
1890
+ ZVAL_STR_COPY (OBJ_PROP_NUM (obj , 0 ), prefix );
1891
+ } else {
1892
+ ZVAL_NULL (OBJ_PROP_NUM (obj , 0 ));
1893
+ }
1894
+
1895
+ if (ns != NULL && ns -> href != NULL && * ns -> href != '\0' ) {
1896
+ ZVAL_STRING (OBJ_PROP_NUM (obj , 1 ), (const char * ) ns -> href );
1897
+ } else {
1898
+ ZVAL_NULL (OBJ_PROP_NUM (obj , 1 ));
1899
+ }
1900
+
1901
+ php_dom_create_object (nodep , OBJ_PROP_NUM (obj , 2 ), intern );
1902
+
1903
+ zend_hash_next_index_insert_new (result , & zv );
1904
+ } ZEND_HASH_FOREACH_END ();
1905
+
1906
+ zend_hash_destroy (& prefix_to_ns_table );
1907
+ }
1908
+
1909
+ PHP_METHOD (Dom_Element , getInScopeNamespaces )
1910
+ {
1911
+ zval * id ;
1912
+ xmlNode * nodep ;
1913
+ dom_object * intern ;
1914
+
1915
+ ZEND_PARSE_PARAMETERS_NONE ();
1916
+
1917
+ DOM_GET_THIS_OBJ (nodep , id , xmlNodePtr , intern );
1918
+
1919
+ php_dom_libxml_ns_mapper * ns_mapper = php_dom_get_ns_mapper (intern );
1920
+
1921
+ array_init (return_value );
1922
+ HashTable * result = Z_ARRVAL_P (return_value );
1923
+
1924
+ dom_element_get_in_scope_namespace_info (ns_mapper , result , nodep , intern );
1925
+ }
1926
+
1927
+ PHP_METHOD (Dom_Element , getDescendantNamespaces )
1928
+ {
1929
+ zval * id ;
1930
+ xmlNode * nodep ;
1931
+ dom_object * intern ;
1932
+
1933
+ ZEND_PARSE_PARAMETERS_NONE ();
1934
+
1935
+ DOM_GET_THIS_OBJ (nodep , id , xmlNodePtr , intern );
1936
+
1937
+ php_dom_libxml_ns_mapper * ns_mapper = php_dom_get_ns_mapper (intern );
1938
+
1939
+ array_init (return_value );
1940
+ HashTable * result = Z_ARRVAL_P (return_value );
1941
+
1942
+ dom_element_get_in_scope_namespace_info (ns_mapper , result , nodep , intern );
1943
+
1944
+ xmlNodePtr cur = nodep -> children ;
1945
+ while (cur != NULL ) {
1946
+ if (cur -> type == XML_ELEMENT_NODE ) {
1947
+ /* TODO: this could be more optimized by updating the same HashTable repeatedly
1948
+ * instead of recreating it on every node. */
1949
+ dom_element_get_in_scope_namespace_info (ns_mapper , result , cur , intern );
1950
+ }
1951
+
1952
+ cur = php_dom_next_in_tree_order (cur , nodep );
1953
+ }
1954
+ }
1955
+
1956
+ PHP_METHOD (Dom_Element , rename )
1957
+ {
1958
+ zend_string * namespace_uri , * qualified_name ;
1959
+ ZEND_PARSE_PARAMETERS_START (2 , 2 )
1960
+ Z_PARAM_STR_OR_NULL (namespace_uri )
1961
+ Z_PARAM_STR (qualified_name )
1962
+ ZEND_PARSE_PARAMETERS_END ();
1963
+
1964
+ zval * id ;
1965
+ dom_object * intern ;
1966
+ xmlNodePtr nodep ;
1967
+ DOM_GET_THIS_OBJ (nodep , id , xmlNodePtr , intern );
1968
+
1969
+ xmlChar * localname = NULL , * prefix = NULL ;
1970
+ int errorcode = dom_validate_and_extract (namespace_uri , qualified_name , & localname , & prefix );
1971
+ if (UNEXPECTED (errorcode != 0 )) {
1972
+ php_dom_throw_error (errorcode , /* strict */ true);
1973
+ goto cleanup ;
1974
+ }
1975
+
1976
+ if (nodep -> type == XML_ATTRIBUTE_NODE ) {
1977
+ /* Check for duplicate attributes. */
1978
+ xmlAttrPtr existing = xmlHasNsProp (nodep -> parent , localname , namespace_uri && ZSTR_VAL (namespace_uri )[0 ] != '\0' ? BAD_CAST ZSTR_VAL (namespace_uri ) : NULL );
1979
+ if (existing != NULL && existing != (xmlAttrPtr ) nodep ) {
1980
+ php_dom_throw_error_with_message (INVALID_MODIFICATION_ERR , "An attribute with the given name in the given namespace already exists" , /* strict */ true);
1981
+ goto cleanup ;
1982
+ }
1983
+ } else {
1984
+ ZEND_ASSERT (nodep -> type == XML_ELEMENT_NODE );
1985
+
1986
+ /* Check for moving to or away from the HTML namespace. */
1987
+ bool is_currently_html_ns = php_dom_ns_is_fast (nodep , php_dom_ns_is_html_magic_token );
1988
+ bool will_be_html_ns = namespace_uri != NULL && zend_string_equals_literal (namespace_uri , DOM_XHTML_NS_URI );
1989
+ if (is_currently_html_ns != will_be_html_ns ) {
1990
+ if (is_currently_html_ns ) {
1991
+ php_dom_throw_error_with_message (
1992
+ INVALID_MODIFICATION_ERR ,
1993
+ "It is not possible to move an element out of the HTML namespace because the HTML namespace is tied to the HTMLElement class" ,
1994
+ /* strict */ true
1995
+ );
1996
+ } else {
1997
+ php_dom_throw_error_with_message (
1998
+ INVALID_MODIFICATION_ERR ,
1999
+ "It is not possible to move an element into the HTML namespace because the HTML namespace is tied to the HTMLElement class" ,
2000
+ /* strict */ true
2001
+ );
2002
+ }
2003
+ goto cleanup ;
2004
+ }
2005
+ }
2006
+
2007
+ php_libxml_invalidate_node_list_cache (intern -> document );
2008
+
2009
+ php_dom_libxml_ns_mapper * ns_mapper = php_dom_get_ns_mapper (intern );
2010
+
2011
+ /* Update namespace uri + prefix by querying the namespace mapper */
2012
+ /* prefix can be NULL here, but that is taken care of by the called APIs. */
2013
+ nodep -> ns = php_dom_libxml_ns_mapper_get_ns_raw_prefix_string (ns_mapper , prefix , xmlStrlen (prefix ), namespace_uri );
2014
+
2015
+ /* Change the local name */
2016
+ if (xmlDictOwns (nodep -> doc -> dict , nodep -> name ) != 1 ) {
2017
+ xmlFree ((xmlChar * ) nodep -> name );
2018
+ }
2019
+ const xmlChar * copy = xmlDictLookup (nodep -> doc -> dict , localname , -1 );
2020
+ if (copy != NULL ) {
2021
+ nodep -> name = copy ;
2022
+ } else {
2023
+ nodep -> name = localname ;
2024
+ localname = NULL ;
2025
+ }
2026
+
2027
+ cleanup :
2028
+ xmlFree (localname );
2029
+ xmlFree (prefix );
2030
+ }
2031
+
1817
2032
#endif
0 commit comments