@@ -170,53 +170,43 @@ public static function create(string $encoding = 'UTF-8'): Dom\XMLDocument
170170 */
171171 public static function normalizeDocument (Dom \XMLDocument $ doc ): Dom \XMLDocument
172172 {
173- // Get the root element
174173 $ root = $ doc ->documentElement ;
175- if ($ root === null ) {
174+ if (! $ root ) {
176175 return $ doc ;
177176 }
178177
179178 $ xpath = XPath::getXPath ($ doc );
180- $ xmlnsAttributes = [];
181-
182- // Collect namespace declarations needed for prefixed elements in the document
183- foreach ($ xpath ->query ('//*[namespace::*] ' ) as $ node ) {
184- if ($ node instanceof Dom \Element) {
185- $ name = 'xmlns: ' . $ node ->prefix ;
186- // Both prefix and namespaceURI NULL equals the default xmlns:xml namespace
187- if ($ node ->prefix !== null && $ node ->namespaceURI !== null ) {
188- $ xmlnsAttributes [$ name ] = $ node ->namespaceURI ;
189- }
190- }
191- }
192179
193- // If no xmlns attributes found, return early with debug info
194- if (empty ($ xmlnsAttributes )) {
195- return $ doc ;
180+ // 1. Collect all xmlns* attributes from the entire document
181+ $ xmlnsAttributes = [];
182+ foreach ($ xpath ->query ('//@*[starts-with(name(), "xmlns")] ' ) as $ attr ) {
183+ $ name = $ attr ->nodeName ; // e.g. "xmlns:ssp" or "xmlns"
184+ $ value = $ attr ->nodeValue ; // the namespace URI
185+ $ xmlnsAttributes [$ name ] = $ value ;
196186 }
197187
198- // Remove xmlns attributes from all elements (proper XMLNS namespace removal)
199- foreach ($ xpath ->query ('//*[namespace::*] ' ) as $ node ) {
200- if (!$ node instanceof Dom \Element) {
201- continue ;
202- }
203-
204- foreach ($ node ->attributes as $ attr ) {
205- if ($ attr ->namespaceURI === C::NS_XMLNS ) {
206- $ node ->removeAttributeNS (C::NS_XMLNS , $ attr ->localName );
207- continue ;
188+ // 2. Remove all xmlns* attributes from every element
189+ foreach ($ xpath ->query ('//* ' ) as $ element ) {
190+ if ($ element instanceof \Dom \Element) { // or DOMElement if still mixed
191+ $ toRemove = [];
192+ foreach ($ element ->attributes as $ attr ) {
193+ if (str_starts_with ($ attr ->nodeName , 'xmlns ' )) {
194+ $ toRemove [] = $ attr ->nodeName ;
195+ }
208196 }
209-
210- if (strpos ($ attr ->nodeName , 'xmlns ' ) === 0 || $ attr ->nodeName === 'xmlns ' ) {
211- // Fallback for implementations that still expose xmlns attrs without namespaceURI
212- $ node ->removeAttribute ($ attr ->nodeName );
197+ foreach ($ toRemove as $ name ) {
198+ $ element ->removeAttribute ($ name );
213199 }
214200 }
215201 }
216202
217- // Add all collected xmlns attributes to the root element
203+ // 3. Re-attach all collected declarations to the root (correctly)
218204 foreach ($ xmlnsAttributes as $ name => $ value ) {
219- $ root ->setAttributeNS (C::NS_XMLNS , $ name , $ value );
205+ $ root ->setAttributeNS (
206+ 'http://www.w3.org/2000/xmlns/ ' ,
207+ $ name ,
208+ $ value
209+ );
220210 }
221211
222212 return $ doc ;
0 commit comments