Skip to content

Conversation

@ioigoume
Copy link
Contributor

No description provided.

@codecov
Copy link

codecov bot commented Nov 13, 2025

Codecov Report

❌ Patch coverage is 73.68421% with 15 lines in your changes missing coverage. Please review.
✅ Project coverage is 93.41%. Comparing base (f4133d8) to head (66bbb30).
⚠️ Report is 10 commits behind head on master.

Additional details and impacted files
@@             Coverage Diff              @@
##             master      #74      +/-   ##
============================================
- Coverage     93.76%   93.41%   -0.35%     
- Complexity      881      913      +32     
============================================
  Files           212      212              
  Lines          3271     3328      +57     
============================================
+ Hits           3067     3109      +42     
- Misses          204      219      +15     
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@tvdijen
Copy link
Member

tvdijen commented Nov 13, 2025

I think it looks really good!
If you are happy with it and finished, I will merge and tag a new release

@ioigoume ioigoume marked this pull request as draft November 13, 2025 10:44
@ioigoume ioigoume marked this pull request as ready for review November 13, 2025 17:01
@ioigoume
Copy link
Contributor Author

ioigoume commented Nov 14, 2025

For reference:

 private static function registerAncestorNamespaces(DOMXPath $xp, DOMNode $node): void
    {
        // Avoid re-binding while walking upwards.
        $registered = [
            'xml' => true,
            'xs'  => true,
        ];

        // Start from the nearest element (or documentElement if a DOMDocument is passed).
        $current = $node instanceof DOMDocument
            ? $node->documentElement
            : ($node instanceof DOMElement ? $node : $node->parentNode);

        // Normalize to the nearest element ancestor (in case $node is a text/attr/etc.)
        while ($current !== null && !$current instanceof DOMElement) {
            $current = $current->parentNode;
        }

        while ($current instanceof DOMElement) {
            // Try to pick up xmlns declarations when they are exposed as attributes.
            if ($current->hasAttributes()) {
                foreach ($current->attributes as $attr) {
                    // Robustly detect namespace declarations:
                    // - prefixed declaration: xmlns:foo="uri" -> $attr->prefix === 'xmlns'
                    // - default declaration:  xmlns="uri"     -> $attr->nodeName === 'xmlns'
                    if ($attr->prefix !== 'xmlns' && $attr->nodeName !== 'xmlns') {
                        continue;
                    }

                    // 'foo' for xmlns:foo, 'xmlns' for default
                    $prefix = ($attr->nodeName === 'xmlns') ? 'xmlns' : $attr->localName;
                    $uri = (string) $attr->nodeValue;

                    if (
                        $prefix === null || $prefix === '' ||
                        $prefix === 'xmlns' || $uri === '' ||
                        isset($registered[$prefix])
                    ) {
                        continue;
                    }

                    $xp->registerNamespace($prefix, $uri);
                    $registered[$prefix] = true;
                }
            }

            // Fallback: bind prefixes in use by elements using their namespaceURI directly.
            // This works even if the subtree is detached (no ancestor xmlns).
            // - current element
            if ($current->prefix !== '' && !isset($registered[$current->prefix])) {
                $uri = $current->namespaceURI; // use the element's own ns URI
                if (is_string($uri) && $uri !== '') {
                    $xp->registerNamespace($current->prefix, $uri);
                    $registered[$current->prefix] = true;
                }
            }
            // attributes (non-xmlns) with prefixes
            if ($current->hasAttributes()) {
                foreach ($current->attributes as $attr) {
                    if (
                        $attr->prefix !== ''
                        && $attr->prefix !== 'xmlns'
                        && !isset($registered[$attr->prefix])
                    ) {
                        $uri = $attr->namespaceURI;
                        if (is_string($uri) && $uri !== '') {
                            $xp->registerNamespace($attr->prefix, $uri);
                            $registered[$attr->prefix] = true;
                        }
                    }
                }
            }
            // all descendant elements: pick prefixes seen anywhere below and bind via their namespaceURI
            $desc = $current->getElementsByTagName('*');
            /** @var \DOMElement $el */
            foreach ($desc as $el) {
                if ($el->prefix === '' || isset($registered[$el->prefix])) {
                    continue;
                }
                $uri = $el->namespaceURI;
                if (is_string($uri) && $uri !== '') {
                    $xp->registerNamespace($el->prefix, $uri);
                    $registered[$el->prefix] = true;
                }
            }

            // Go up one step; root's parent is DOMDocument, which stops the loop
            $current = $current->parentNode;
        }
    }

@tvdijen tvdijen merged commit 4988cc7 into simplesamlphp:master Nov 14, 2025
22 checks passed
tvdijen added a commit that referenced this pull request Nov 14, 2025
@ioigoume ioigoume deleted the xpath-ancestor-xmlns-registration branch November 15, 2025 16:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants