@@ -2081,6 +2081,82 @@ PHP_METHOD(DOMNode, lookupNamespaceURI)
20812081}
20822082/* }}} end dom_node_lookup_namespace_uri */
20832083
2084+ static void dom_relink_ns_decls_element (HashTable * links , xmlNodePtr node )
2085+ {
2086+ if (node -> type == XML_ELEMENT_NODE ) {
2087+ for (xmlAttrPtr attr = node -> properties ; attr ; attr = attr -> next ) {
2088+ if (php_dom_ns_is_fast ((const xmlNode * ) attr , php_dom_ns_is_xmlns_magic_token )) {
2089+ xmlNsPtr ns = xmlMalloc (sizeof (* ns ));
2090+ if (!ns ) {
2091+ continue ;
2092+ }
2093+
2094+ bool should_free ;
2095+ xmlChar * attr_value = php_libxml_attr_value (attr , & should_free );
2096+
2097+ memset (ns , 0 , sizeof (* ns ));
2098+ ns -> type = XML_NAMESPACE_DECL ;
2099+ ns -> href = should_free ? attr_value : xmlStrdup (attr_value );
2100+ ns -> prefix = attr -> ns -> prefix ? xmlStrdup (attr -> name ) : NULL ;
2101+ ns -> next = node -> nsDef ;
2102+ node -> nsDef = ns ;
2103+
2104+ ns -> _private = attr ;
2105+ if (attr -> prev ) {
2106+ attr -> prev = attr -> next ;
2107+ } else {
2108+ node -> properties = attr -> next ;
2109+ }
2110+ if (attr -> next ) {
2111+ attr -> next -> prev = attr -> prev ;
2112+ }
2113+
2114+ zval * zv = zend_hash_index_lookup (links , (zend_ulong ) node );
2115+ if (Z_ISNULL_P (zv )) {
2116+ ZVAL_LONG (zv , 1 );
2117+ } else {
2118+ Z_LVAL_P (zv )++ ;
2119+ }
2120+ }
2121+ }
2122+ }
2123+ }
2124+
2125+ static void dom_relink_ns_decls (HashTable * links , xmlNodePtr root )
2126+ {
2127+ dom_relink_ns_decls_element (links , root );
2128+
2129+ xmlNodePtr base = root ;
2130+ xmlNodePtr node = base -> children ;
2131+ while (node != NULL ) {
2132+ dom_relink_ns_decls_element (links , node );
2133+ node = php_dom_next_in_tree_order (node , base );
2134+ }
2135+ }
2136+
2137+ static void dom_unlink_ns_decls (HashTable * links )
2138+ {
2139+ ZEND_HASH_MAP_FOREACH_NUM_KEY_VAL (links , zend_ulong h , zval * counter ) {
2140+ xmlNodePtr node = (xmlNodePtr ) h ;
2141+ while (Z_LVAL_P (counter )-- > 0 ) {
2142+ xmlNsPtr ns = node -> nsDef ;
2143+ node -> nsDef = node -> nsDef -> next ;
2144+
2145+ xmlAttrPtr attr = ns -> _private ;
2146+ if (attr -> prev ) {
2147+ attr -> prev -> next = attr ;
2148+ } else {
2149+ node -> properties = attr ;
2150+ }
2151+ if (attr -> next ) {
2152+ attr -> next -> prev = attr ;
2153+ }
2154+
2155+ xmlFreeNs (ns );
2156+ }
2157+ } ZEND_HASH_FOREACH_END ();
2158+ }
2159+
20842160static int dom_canonicalize_node_parent_lookup_cb (void * user_data , xmlNodePtr node , xmlNodePtr parent )
20852161{
20862162 xmlNodePtr root = user_data ;
@@ -2136,7 +2212,23 @@ static void dom_canonicalization(INTERNAL_FUNCTION_PARAMETERS, int mode) /* {{{
21362212
21372213 docp = nodep -> doc ;
21382214
2139- if (! docp ) {
2215+ HashTable links ;
2216+ bool modern = php_dom_follow_spec_node (nodep );
2217+ if (modern ) {
2218+ xmlNodePtr root = nodep ;
2219+ while (root -> parent ) {
2220+ root = root -> parent ;
2221+ }
2222+
2223+ if (UNEXPECTED (root -> type != XML_DOCUMENT_NODE && root -> type != XML_HTML_DOCUMENT_NODE )) {
2224+ php_dom_throw_error_with_message (HIERARCHY_REQUEST_ERR , "Canonicalization can only happen on nodes attached to a document." , /* strict */ true);
2225+ RETURN_THROWS ();
2226+ }
2227+
2228+ zend_hash_init (& links , 0 , NULL , NULL , false);
2229+ dom_relink_ns_decls (& links , xmlDocGetRootElement (docp ));
2230+ } else if (!docp ) {
2231+ /* Note: not triggerable with modern DOM */
21402232 zend_throw_error (NULL , "Node must be associated with a document" );
21412233 RETURN_THROWS ();
21422234 }
@@ -2158,12 +2250,12 @@ static void dom_canonicalization(INTERNAL_FUNCTION_PARAMETERS, int mode) /* {{{
21582250 if (!tmp ) {
21592251 /* if mode == 0 then $xpath arg is 3, if mode == 1 then $xpath is 4 */
21602252 zend_argument_value_error (3 + mode , "must have a \"query\" key" );
2161- RETURN_THROWS () ;
2253+ goto clean_links ;
21622254 }
21632255 if (Z_TYPE_P (tmp ) != IS_STRING ) {
21642256 /* if mode == 0 then $xpath arg is 3, if mode == 1 then $xpath is 4 */
21652257 zend_argument_type_error (3 + mode , "\"query\" option must be a string, %s given" , zend_zval_value_name (tmp ));
2166- RETURN_THROWS () ;
2258+ goto clean_links ;
21672259 }
21682260 xquery = Z_STRVAL_P (tmp );
21692261
@@ -2195,7 +2287,7 @@ static void dom_canonicalization(INTERNAL_FUNCTION_PARAMETERS, int mode) /* {{{
21952287 }
21962288 xmlXPathFreeContext (ctxp );
21972289 zend_throw_error (NULL , "XPath query did not return a nodeset" );
2198- RETURN_THROWS () ;
2290+ goto clean_links ;
21992291 }
22002292 }
22012293
@@ -2264,6 +2356,12 @@ static void dom_canonicalization(INTERNAL_FUNCTION_PARAMETERS, int mode) /* {{{
22642356 RETURN_LONG (bytes );
22652357 }
22662358 }
2359+
2360+ clean_links :
2361+ if (modern ) {
2362+ dom_unlink_ns_decls (& links );
2363+ zend_hash_destroy (& links );
2364+ }
22672365}
22682366/* }}} */
22692367
0 commit comments