Passed
Pull Request — master (#337)
by Tim
02:20
created

AbstractEndpointType::validateArray()   A

Complexity

Conditions 5
Paths 8

Size

Total Lines 31
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 17
nc 8
nop 1
dl 0
loc 31
rs 9.3888
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\SAML2\XML\md;
6
7
use DOMElement;
8
use SimpleSAML\Assert\Assert;
9
use SimpleSAML\SAML2\Constants as C;
10
use SimpleSAML\XML\ArrayizableElementInterface;
11
use SimpleSAML\XML\Attribute as XMLAttribute;
12
use SimpleSAML\XML\Chunk;
13
use SimpleSAML\XML\Exception\InvalidDOMElementException;
14
use SimpleSAML\XML\Exception\SchemaViolationException;
15
use SimpleSAML\XML\ExtendableAttributesTrait;
16
use SimpleSAML\XML\ExtendableElementTrait;
17
use SimpleSAML\XML\SerializableElementInterface;
18
19
use function array_key_exists;
20
use function array_keys;
21
22
/**
23
 * Class representing SAML 2 EndpointType.
24
 *
25
 * This class can be used in two different ways:
26
 *
27
 *   - You can extend the class without extending the constructor. Then you can use the methods available and the class
28
 *     will generate an element with the same name as the extending class
29
 *     (e.g. \SimpleSAML\SAML2\XML\md\AttributeService).
30
 *
31
 *   - Alternatively, you may want to extend the type to add new attributes (e.g look at IndexedEndpointType). In that
32
 *     case, you cannot use this class normally, as if you change the signature of the constructor, you cannot call
33
 *     fromXML() in this class. In order to process an XML document, you can use the get*Attribute() static methods
34
 *     from AbstractElement, and reimplement the fromXML() method with them to suit your new constructor.
35
 *
36
 * @package simplesamlphp/saml2
37
 */
38
abstract class AbstractEndpointType extends AbstractMdElement implements ArrayizableElementInterface
39
{
40
    use ExtendableAttributesTrait;
1 ignored issue
show
introduced by
The trait SimpleSAML\XML\ExtendableAttributesTrait requires some properties which are not provided by SimpleSAML\SAML2\XML\md\AbstractEndpointType: $localName, $nodeValue, $namespaceURI, $prefix, $attributes
Loading history...
41
    use ExtendableElementTrait;
42
43
    /** The namespace-attribute for the xs:any element */
44
    public const XS_ANY_ELT_NAMESPACE = C::XS_ANY_NS_OTHER;
45
46
    /** The namespace-attribute for the xs:anyAttribute element */
47
    public const XS_ANY_ATTR_NAMESPACE = C::XS_ANY_NS_OTHER;
48
49
50
    /**
51
     * EndpointType constructor.
52
     *
53
     * @param string $binding
54
     * @param string $location
55
     * @param string|null $responseLocation
56
     * @param list<\SimpleSAML\XML\Attribute> $attributes
0 ignored issues
show
Bug introduced by
The type SimpleSAML\SAML2\XML\md\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...
57
     * @param \SimpleSAML\XML\ElementInterface[] $children
58
     *
59
     * @throws \SimpleSAML\Assert\AssertionFailedException
60
     */
61
    public function __construct(
62
        protected string $binding,
63
        protected string $location,
64
        protected ?string $responseLocation = null,
65
        array $attributes = [],
66
        array $children = [],
67
    ) {
68
        Assert::validURI($binding, SchemaViolationException::class); // Covers the empty string
69
        Assert::validURI($location, SchemaViolationException::class); // Covers the empty string
70
        Assert::nullOrValidURI($responseLocation, SchemaViolationException::class); // Covers the empty string
71
72
        $this->setAttributesNS($attributes);
73
        $this->setElements($children);
74
    }
75
76
77
    /**
78
     * Collect the value of the Binding property.
79
     *
80
     * @return string
81
     */
82
    public function getBinding(): string
83
    {
84
        return $this->binding;
85
    }
86
87
88
    /**
89
     * Collect the value of the Location property.
90
     *
91
     * @return string
92
     */
93
    public function getLocation(): string
94
    {
95
        return $this->location;
96
    }
97
98
99
    /**
100
     * Collect the value of the ResponseLocation property.
101
     *
102
     * @return string|null
103
     */
104
    public function getResponseLocation(): ?string
105
    {
106
        return $this->responseLocation;
107
    }
108
109
110
    /**
111
     * Initialize an EndpointType.
112
     *
113
     * Note: this method cannot be used when extending this class, if the constructor has a different signature.
114
     *
115
     * @param \DOMElement $xml The XML element we should load.
116
     * @return static
117
     *
118
     * @throws \SimpleSAML\XML\Exception\InvalidDOMElementException
119
     *   if the qualified name of the supplied element is wrong
120
     * @throws \SimpleSAML\XML\Exception\MissingAttributeException
121
     *   if the supplied element is missing any of the mandatory attributes
122
     */
123
    public static function fromXML(DOMElement $xml): static
124
    {
125
        $qualifiedName = static::getClassName(static::class);
126
        Assert::eq(
127
            $xml->localName,
128
            $qualifiedName,
129
            'Unexpected name for endpoint: ' . $xml->localName . '. Expected: ' . $qualifiedName . '.',
130
            InvalidDOMElementException::class,
131
        );
132
133
        $binding = self::getAttribute($xml, 'Binding');
134
        $location = self::getAttribute($xml, 'Location');
135
136
        $children = [];
137
        foreach ($xml->childNodes as $child) {
138
            if ($child->namespaceURI === C::NS_MD) {
139
                continue;
140
            } elseif (!($child instanceof DOMElement)) {
141
                continue;
142
            }
143
144
            $children[] = new Chunk($child);
145
        }
146
147
        return new static(
148
            $binding,
149
            $location,
150
            self::getOptionalAttribute($xml, 'ResponseLocation', null),
151
            self::getAttributesNSFromXML($xml),
152
            $children,
153
        );
154
    }
155
156
157
    /**
158
     * Add this endpoint to an XML element.
159
     *
160
     * @param \DOMElement $parent The element we should append this endpoint to.
161
     * @return \DOMElement
162
     */
163
    public function toXML(DOMElement $parent = null): DOMElement
164
    {
165
        $e = parent::instantiateParentElement($parent);
166
167
        $e->setAttribute('Binding', $this->getBinding());
168
        $e->setAttribute('Location', $this->getLocation());
169
170
        if ($this->getResponseLocation() !== null) {
171
            $e->setAttribute('ResponseLocation', $this->getResponseLocation());
172
        }
173
174
        foreach ($this->getAttributesNS() as $attr) {
175
            $attr->toXML($e);
176
        }
177
178
        /** @var \SimpleSAML\XML\SerializableElementInterface $child */
179
        foreach ($this->getElements() as $child) {
180
            if (!$child->isEmptyElement()) {
181
                $child->toXML($e);
182
            }
183
        }
184
185
        return $e;
186
    }
187
188
189
    /**
190
     * Create a class from an array
191
     *
192
     * @param array $data
193
     * @return static
194
     */
195
    public static function fromArray(array $data): static
196
    {
197
        self::validateArray($data);
198
199
        $responseLocation = array_key_exists('ResponseLocation', $data) ? $data['ResponseLocation'] : null;
200
        $Extensions = array_key_exists('Extensions', $data) ? $data['Extensions'] : null;
201
202
        $attributes = [];
203
        if (array_key_exists('attributes', $data)) {
204
            foreach ($data['attributes'] as $attr) {
205
                $attributes[] = new XMLAttribute(
206
                    $attr['namespaceURI'],
207
                    $attr['namespacePrefix'],
208
                    $attr['attrName'],
209
                    $attr['attrValue'],
210
                );
211
            }
212
        }
213
214
        return new static($data['Binding'], $data['Location'], $responseLocation, $attributes, $Extensions);
0 ignored issues
show
Bug introduced by
It seems like $Extensions can also be of type null; however, parameter $children of SimpleSAML\SAML2\XML\md\...ointType::__construct() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

214
        return new static($data['Binding'], $data['Location'], $responseLocation, $attributes, /** @scrutinizer ignore-type */ $Extensions);
Loading history...
215
    }
216
217
218
    /**
219
     * Validate an array
220
     *
221
     * @param array $data
222
     * @return void
223
     */
224
    public static function validateArray(array $data): void
225
    {
226
        // Make sure the array keys are known for this kind of object
227
        Assert::allOneOf(
228
            array_keys($data),
229
            ['Binding', 'Location', 'ResponseLocation', 'attributes', 'Extensions'],
230
        );
231
232
        // Make sure all the mandatory items exist
233
        Assert::keyExists($data, 'Binding');
234
        Assert::keyExists($data, 'Location');
235
236
        // Make sure the items have the correct data type
237
        Assert::string($data['Binding']);
238
        Assert::string($data['Location']);
239
240
        if (array_key_exists('ResponseLocation', $data)) {
241
            Assert::string($data['ResponseLocation']);
242
        }
243
244
        if (array_key_exists('attributes', $data)) {
245
            Assert::isArray($data['attributes']);
246
            Assert::allIsArray($data['attributes']);
247
            foreach ($data['attributes'] as $attr) {
248
                XMLAttribute::validateArray($attr);
249
            }
250
        }
251
252
        if (array_key_exists('Extensions', $data)) {
253
            Assert::isArray($data['Extensions']);
254
            Assert::allIsInstanceOf($data['Extensions'], SerializableElementInterface::class);
255
        }
256
    }
257
258
259
    /**
260
     * Create an array from this class
261
     *
262
     * @return array
263
     */
264
    public function toArray(): array
265
    {
266
        $data = [
267
            'Binding' => $this->getBinding(),
268
            'Location' => $this->getLocation(),
269
            'ResponseLocation' => $this->getResponseLocation(),
270
            'Extensions' => $this->getElements(),
271
        ];
272
273
        foreach ($this->getAttributesNS() as $a) {
274
            $data['attributes'][] = $a->toArray();
275
        }
276
277
        return array_filter($data);
278
    }
279
}
280