@@ -2103,65 +2103,71 @@ PHP_METHOD(DOMNode, lookupNamespaceURI)
21032103}
21042104/* }}} end dom_node_lookup_namespace_uri */
21052105
2106+ /* Allocate, track and prepend a temporary nsDef entry for C14N.
2107+ * Returns the new xmlNsPtr for the caller to fill in href/prefix/_private,
2108+ * or NULL on allocation failure. */
2109+ static xmlNsPtr dom_alloc_ns_decl (HashTable * links , xmlNodePtr node )
2110+ {
2111+ xmlNsPtr ns = xmlMalloc (sizeof (* ns ));
2112+ if (!ns ) {
2113+ return NULL ;
2114+ }
2115+
2116+ zval * zv = zend_hash_index_lookup (links , (zend_ulong ) node );
2117+ if (Z_ISNULL_P (zv )) {
2118+ ZVAL_LONG (zv , 1 );
2119+ } else {
2120+ Z_LVAL_P (zv )++ ;
2121+ }
2122+
2123+ memset (ns , 0 , sizeof (* ns ));
2124+ ns -> type = XML_LOCAL_NAMESPACE ;
2125+ ns -> next = node -> nsDef ;
2126+ node -> nsDef = ns ;
2127+
2128+ return ns ;
2129+ }
2130+
21062131/* For DOM-built documents (e.g. createElementNS), namespaces live on node->ns
21072132 * without corresponding xmlns attributes. C14N expects nsDef entries, so create
21082133 * temporary ones. The _private field stays NULL to distinguish from attr-based
21092134 * entries during cleanup. */
21102135static void dom_add_synthetic_ns_decl (HashTable * links , xmlNodePtr node , xmlNsPtr src_ns )
21112136{
2137+ /* Pre-existing nsDef entries are never filled in for Dom\XMLDocument.
2138+ * This loop guards against duplicates from our own synthetic additions
2139+ * within the same C14N pass (e.g. when an element and its attributes
2140+ * share the same namespace prefix). */
21122141 for (xmlNsPtr existing = node -> nsDef ; existing ; existing = existing -> next ) {
21132142 if (xmlStrEqual (existing -> prefix , src_ns -> prefix )) {
21142143 return ;
21152144 }
21162145 }
21172146
2118- xmlNsPtr ns = xmlMalloc ( sizeof ( * ns ) );
2147+ xmlNsPtr ns = dom_alloc_ns_decl ( links , node );
21192148 if (!ns ) {
21202149 return ;
21212150 }
21222151
2123- zval * zv = zend_hash_index_lookup (links , (zend_ulong ) node );
2124- if (Z_ISNULL_P (zv )) {
2125- ZVAL_LONG (zv , 1 );
2126- } else {
2127- Z_LVAL_P (zv )++ ;
2128- }
2129-
2130- memset (ns , 0 , sizeof (* ns ));
2131- ns -> type = XML_LOCAL_NAMESPACE ;
21322152 ns -> href = xmlStrdup (src_ns -> href );
21332153 ns -> prefix = src_ns -> prefix ? xmlStrdup (src_ns -> prefix ) : NULL ;
2134- ns -> next = node -> nsDef ;
2135- node -> nsDef = ns ;
21362154}
21372155
21382156static void dom_relink_ns_decls_element (HashTable * links , xmlNodePtr node )
21392157{
21402158 if (node -> type == XML_ELEMENT_NODE ) {
21412159 for (xmlAttrPtr attr = node -> properties ; attr ; attr = attr -> next ) {
21422160 if (php_dom_ns_is_fast ((const xmlNode * ) attr , php_dom_ns_is_xmlns_magic_token )) {
2143- xmlNsPtr ns = xmlMalloc ( sizeof ( * ns ) );
2161+ xmlNsPtr ns = dom_alloc_ns_decl ( links , node );
21442162 if (!ns ) {
21452163 return ;
21462164 }
21472165
2148- zval * zv = zend_hash_index_lookup (links , (zend_ulong ) node );
2149- if (Z_ISNULL_P (zv )) {
2150- ZVAL_LONG (zv , 1 );
2151- } else {
2152- Z_LVAL_P (zv )++ ;
2153- }
2154-
21552166 bool should_free ;
21562167 xmlChar * attr_value = php_libxml_attr_value (attr , & should_free );
21572168
2158- memset (ns , 0 , sizeof (* ns ));
2159- ns -> type = XML_LOCAL_NAMESPACE ;
21602169 ns -> href = should_free ? attr_value : xmlStrdup (attr_value );
21612170 ns -> prefix = attr -> ns -> prefix ? xmlStrdup (attr -> name ) : NULL ;
2162- ns -> next = node -> nsDef ;
2163- node -> nsDef = ns ;
2164-
21652171 ns -> _private = attr ;
21662172 if (attr -> prev ) {
21672173 attr -> prev = attr -> next ;
0 commit comments