Completed
Push — master ( ed1e7e...e83944 )
by Tim
16s queued 13s
created

ExtendableAttributesTrait   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 171
Duplicated Lines 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
wmc 21
eloc 58
c 3
b 0
f 0
dl 0
loc 171
rs 10

6 Methods

Rating   Name   Duplication   Size   Complexity  
A getAttributesNSFromXML() 0 11 3
A hasAttributeNS() 0 8 4
A getAttributeNS() 0 8 4
A getAttributesNS() 0 3 1
A getAttributeNamespace() 0 10 1
B setAttributesNS() 0 72 8
1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\XML;
6
7
use DOMElement;
8
use RuntimeException;
9
use SimpleSAML\Assert\Assert;
10
use SimpleSAML\XML\Attribute;
11
use SimpleSAML\XML\Constants as C;
12
13
use function array_diff;
14
use function array_map;
15
use function array_search;
16
use function defined;
17
use function implode;
18
use function is_array;
19
use function rtrim;
20
use function sprintf;
21
22
/**
23
 * Trait for elements that can have arbitrary namespaced attributes.
24
 *
25
 * @package simplesamlphp/xml-common
26
 */
27
trait ExtendableAttributesTrait
28
{
29
    /**
30
     * Extra (namespace qualified) attributes.
31
     *
32
     * @var list<\SimpleSAML\XML\Attribute>
1 ignored issue
show
Bug introduced by
The type SimpleSAML\XML\list was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
33
     */
34
    protected array $namespacedAttributes = [];
35
36
37
    /**
38
     * Check if a namespace-qualified attribute exists.
39
     *
40
     * @param string|null $namespaceURI The namespace URI.
41
     * @param string $localName The local name.
42
     * @return bool true if the attribute exists, false if not.
43
     */
44
    public function hasAttributeNS(?string $namespaceURI, string $localName): bool
45
    {
46
        foreach ($this->getAttributesNS() as $attr) {
47
            if ($attr->getNamespaceURI() === $namespaceURI && $attr->getAttrName() === $localName) {
48
                return true;
49
            }
50
        }
51
        return false;
52
    }
53
54
55
    /**
56
     * Get a namespace-qualified attribute.
57
     *
58
     * @param string|null $namespaceURI The namespace URI.
59
     * @param string $localName The local name.
60
     * @return \SimpleSAML\XML\Attribute|null The value of the attribute, or null if the attribute does not exist.
61
     */
62
    public function getAttributeNS(?string $namespaceURI, string $localName): ?Attribute
63
    {
64
        foreach ($this->getAttributesNS() as $attr) {
65
            if ($attr->getNamespaceURI() === $namespaceURI && $attr->getAttrName() === $localName) {
66
                return $attr;
67
            }
68
        }
69
        return null;
70
    }
71
72
73
    /**
74
     * Get the namespaced attributes in this element.
75
     *
76
     * @return list<\SimpleSAML\XML\Attribute>
77
     */
78
    public function getAttributesNS(): array
79
    {
80
        return $this->namespacedAttributes;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->namespacedAttributes returns the type array which is incompatible with the documented return type SimpleSAML\XML\list.
Loading history...
81
    }
82
83
84
    /**
85
     * Parse an XML document and get the namespaced attributes.
86
     *
87
     * @param \DOMElement $xml
88
     *
89
     * @return list<\SimpleSAML\XML\Attribute> $attributes
90
     */
91
    protected static function getAttributesNSFromXML(DOMElement $xml): array
92
    {
93
        $attributes = [];
94
95
        foreach ($xml->attributes as $a) {
96
            if ($a->namespaceURI !== null) {
97
                $attributes[] = new Attribute($a->namespaceURI, $a->prefix, $a->localName, $a->nodeValue);
98
            }
99
        }
100
101
        return $attributes;
102
    }
103
104
105
    /**
106
     * @param list<\SimpleSAML\XML\Attribute> $attributes
107
     * @throws \SimpleSAML\Assert\AssertionFailedException if $attributes contains anything other than Attribute objects
108
     */
109
    protected function setAttributesNS(array $attributes): void
110
    {
111
        Assert::allIsInstanceOf(
112
            $attributes,
113
            Attribute::class,
114
            'Arbitrary XML attributes can only be an instance of Attribute.',
115
        );
116
        $namespace = $this->getAttributeNamespace();
117
118
        // Validate namespace value
119
        if (!is_array($namespace)) {
120
            // Must be one of the predefined values
121
            Assert::oneOf($namespace, C::XS_ANY_NS);
122
        } else {
123
            // Array must be non-empty and cannot contain ##any or ##other
124
            Assert::notEmpty($namespace);
125
            Assert::allNotSame($namespace, C::XS_ANY_NS_ANY);
126
            Assert::allNotSame($namespace, C::XS_ANY_NS_OTHER);
127
        }
128
129
        // Get namespaces for all attributes
130
        $actual_namespaces = array_map(
131
            /**
132
             * @param \SimpleSAML\XML\Attribute $elt
133
             * @return string|null
134
             */
135
            function (Attribute $attr) {
136
                return $attr->getNamespaceURI();
137
            },
138
            $attributes,
139
        );
140
141
        if ($namespace === C::XS_ANY_NS_LOCAL) {
142
            // If ##local then all namespaces must be null
143
            Assert::allNull($actual_namespaces);
144
        } elseif (is_array($namespace)) {
145
            // Make a local copy of the property that we can edit
146
            $allowed_namespaces = $namespace;
147
148
            // Replace the ##targetedNamespace with the actual namespace
149
            if (($key = array_search(C::XS_ANY_NS_TARGET, $allowed_namespaces)) !== false) {
150
                $allowed_namespaces[$key] = static::NS;
1 ignored issue
show
Bug introduced by
The constant SimpleSAML\XML\ExtendableAttributesTrait::NS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
151
            }
152
153
            // Replace the ##local with null
154
            if (($key = array_search(C::XS_ANY_NS_LOCAL, $allowed_namespaces)) !== false) {
155
                $allowed_namespaces[$key] = null;
156
            }
157
158
            $diff = array_diff($actual_namespaces, $allowed_namespaces);
159
            Assert::isEmpty(
160
                $diff,
161
                sprintf(
162
                    'Attributes from namespaces [ %s ] are not allowed inside a %s element.',
163
                    rtrim(implode(', ', $diff)),
164
                    static::NS,
165
                ),
166
            );
167
        } else {
168
            // All elements must be namespaced, ergo non-null
169
            Assert::allNotNull($actual_namespaces);
170
171
            if ($namespace === C::XS_ANY_NS_OTHER) {
172
                // Must be any namespace other than the parent element
173
                Assert::allNotSame($actual_namespaces, static::NS);
174
            } elseif ($namespace === C::XS_ANY_NS_TARGET) {
175
                // Must be the same namespace as the one of the parent element
176
                Assert::allSame($actual_namespaces, static::NS);
177
            }
178
        }
179
180
        $this->namespacedAttributes = $attributes;
0 ignored issues
show
Documentation Bug introduced by
It seems like $attributes of type array is incompatible with the declared type SimpleSAML\XML\list of property $namespacedAttributes.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
181
    }
182
183
184
185
    /**
186
     * @return array|string
187
     */
188
    public function getAttributeNamespace(): array|string
189
    {
190
        Assert::true(
191
            defined('static::XS_ANY_ATTR_NAMESPACE'),
192
            self::getClassName(static::class)
193
            . '::XS_ANY_ATTR_NAMESPACE constant must be defined and set to the namespace for the xs:anyAttribute.',
194
            RuntimeException::class,
195
        );
196
197
        return static::XS_ANY_ATTR_NAMESPACE;
1 ignored issue
show
Bug introduced by
The constant SimpleSAML\XML\Extendabl...::XS_ANY_ATTR_NAMESPACE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
198
    }
199
}
200