AbstractIndexedEndpointType::fromArray()   A
last analyzed

Complexity

Conditions 3
Paths 1

Size

Total Lines 12
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 9
nc 1
nop 1
dl 0
loc 12
rs 9.9666
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\SAML2\Assert\Assert;
9
use SimpleSAML\SAML2\Exception\ArrayValidationException;
10
use SimpleSAML\SAML2\Type\SAMLAnyURIValue;
11
use SimpleSAML\XML\ArrayizableElementInterface;
12
use SimpleSAML\XML\Attribute as XMLAttribute;
13
use SimpleSAML\XML\SerializableElementInterface;
14
use SimpleSAML\XMLSchema\Exception\InvalidDOMElementException;
15
use SimpleSAML\XMLSchema\Type\BooleanValue;
0 ignored issues
show
Bug introduced by
The type SimpleSAML\XMLSchema\Type\BooleanValue 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...
16
use SimpleSAML\XMLSchema\Type\UnsignedShortValue;
0 ignored issues
show
Bug introduced by
The type SimpleSAML\XMLSchema\Type\UnsignedShortValue 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...
17
18
use function array_filter;
19
use function array_key_exists;
20
use function array_keys;
21
22
/**
23
 * Class representing a SAML2 IndexedEndpointType.
24
 *
25
 * @package simplesamlphp/saml2
26
 */
27
abstract class AbstractIndexedEndpointType extends AbstractEndpointType implements ArrayizableElementInterface
0 ignored issues
show
Bug introduced by
The type SimpleSAML\SAML2\XML\md\AbstractEndpointType 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...
28
{
29
    use IndexedElementTrait;
30
31
32
    /**
33
     * IndexedEndpointType constructor.
34
     *
35
     * Note: if you extend this class, the constructor must retain its signature. You cannot extend this class and
36
     * modify the signature of the constructor, unless you implement fromXML() yourself. This class provides
37
     * static methods to get its properties from a given \DOMElement for your convenience. Look at the implementation
38
     * of fromXML() to know how to use them.
39
     *
40
     * @param \SimpleSAML\XMLSchema\Type\UnsignedShortValue $index
41
     * @param \SimpleSAML\SAML2\Type\SAMLAnyURIValue $binding
42
     * @param \SimpleSAML\SAML2\Type\SAMLAnyURIValue $location
43
     * @param \SimpleSAML\XMLSchema\Type\BooleanValue|null $isDefault
44
     * @param \SimpleSAML\SAML2\Type\SAMLAnyURIValue|null $responseLocation
45
     * @param array $children
46
     * @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...
47
     */
48
    public function __construct(
49
        UnsignedShortValue $index,
50
        SAMLAnyURIValue $binding,
51
        SAMLAnyURIValue $location,
52
        ?BooleanValue $isDefault = null,
53
        ?SAMLAnyURIValue $responseLocation = null,
54
        array $children = [],
55
        array $attributes = [],
56
    ) {
57
        parent::__construct($binding, $location, $responseLocation, $children, $attributes);
58
59
        $this->setIndex($index);
60
        $this->setIsDefault($isDefault);
61
    }
62
63
64
    /**
65
     * Initialize an IndexedEndpointType.
66
     *
67
     * @param \DOMElement $xml The XML element we should load.
68
     * @return static
69
     *
70
     * @throws \SimpleSAML\XMLSchema\Exception\InvalidDOMElementException
71
     *   if the qualified name of the supplied element is wrong
72
     * @throws \SimpleSAML\XMLSchema\Exception\MissingAttributeException
73
     *   if the supplied element is missing any of the mandatory attributes
74
     */
75
    public static function fromXML(DOMElement $xml): static
76
    {
77
        $qualifiedName = static::getClassName(static::class);
78
        Assert::eq(
79
            $xml->localName,
80
            $qualifiedName,
81
            'Unexpected name for endpoint: ' . $xml->localName . '. Expected: ' . $qualifiedName . '.',
82
            InvalidDOMElementException::class,
83
        );
84
85
        return new static(
86
            self::getAttribute($xml, 'index', UnsignedShortValue::class),
87
            self::getAttribute($xml, 'Binding', SAMLAnyURIValue::class),
88
            self::getAttribute($xml, 'Location', SAMLAnyURIValue::class),
89
            self::getOptionalAttribute($xml, 'isDefault', BooleanValue::class, null),
90
            self::getOptionalAttribute($xml, 'ResponseLocation', SAMLAnyURIValue::class, null),
91
            self::getChildElementsFromXML($xml),
92
            self::getAttributesNSFromXML($xml),
93
        );
94
    }
95
96
97
    /**
98
     * Add this endpoint to an XML element.
99
     *
100
     * @param \DOMElement $parent The element we should append this endpoint to.
101
     */
102
    public function toXML(?DOMElement $parent = null): DOMElement
103
    {
104
        $e = parent::instantiateParentElement($parent);
105
        $e->setAttribute('Binding', $this->getBinding()->getValue());
106
        $e->setAttribute('Location', $this->getLocation()->getValue());
107
108
        if ($this->getResponseLocation() !== null) {
109
            $e->setAttribute('ResponseLocation', $this->getResponseLocation()->getValue());
110
        }
111
112
        $e->setAttribute('index', $this->getIndex()->getValue());
113
114
        if ($this->getIsDefault() !== null) {
115
            $e->setAttribute('isDefault', $this->getIsDefault()->getValue());
116
        }
117
118
        foreach ($this->getAttributesNS() as $attr) {
119
            $attr->toXML($e);
120
        }
121
122
        /** @var \SimpleSAML\XML\SerializableElementInterface $child */
123
        foreach ($this->getElements() as $child) {
124
            if (!$child->isEmptyElement()) {
125
                $child->toXML($e);
126
            }
127
        }
128
129
        return $e;
130
    }
131
132
133
    /**
134
     * Create a class from an array
135
     *
136
     * @param array{
137
     *   'index': int,
138
     *   'Binding': string,
139
     *   'Location': string,
140
     *   'isDefault'?: bool,
141
     *   'ResponseLocation'?: string,
142
     *   'children'?: array,
143
     *   'attributes'?: array,
144
     * } $data
145
     */
146
    public static function fromArray(array $data): static
147
    {
148
        $data = self::processArrayContents($data);
149
150
        return new static(
151
            UnsignedShortValue::fromInteger($data['index']),
152
            SAMLAnyURIValue::fromString($data['Binding']),
153
            SAMLAnyURIValue::fromString($data['Location']),
154
            $data['isDefault'] !== null ? BooleanValue::fromBoolean($data['isDefault']) : null,
155
            ($data['ResponseLocation'] ?? null) ? SAMLAnyURIValue::fromString($data['ResponseLocation']) : null,
156
            $data['children'] ?? [],
157
            $data['attributes'] ?? [],
158
        );
159
    }
160
161
162
    /**
163
     * Validates an array representation of this object and returns the same array with
164
     * rationalized keys (casing) and parsed sub-elements.
165
     *
166
     * @param array{
167
     *   'index': int,
168
     *   'Binding': string,
169
     *   'Location': string,
170
     *   'isDefault'?: bool,
171
     *   'ResponseLocation'?: string,
172
     *   'children'?: array,
173
     *   'attributes'?: array,
174
     * } $data
175
     * @return array{
0 ignored issues
show
Documentation Bug introduced by
The doc comment array{ at position 2 could not be parsed: the token is null at position 2.
Loading history...
176
     *   'index': int,
177
     *   'Binding': string,
178
     *   'Location': string,
179
     *   'isDefault'?: bool,
180
     *   'ResponseLocation'?: string,
181
     *   'children'?: array,
182
     *   'attributes'?: array,
183
     * }
184
     */
185
    private static function processArrayContents(array $data): array
186
    {
187
        $data = array_change_key_case($data, CASE_LOWER);
188
189
        // Make sure the array keys are known for this kind of object
190
        Assert::allOneOf(
191
            array_keys($data),
192
            ['index', 'binding', 'location', 'isdefault', 'responselocation', 'children', 'attributes'],
193
            ArrayValidationException::class,
194
        );
195
196
        // Make sure all the mandatory items exist
197
        Assert::keyExists($data, 'binding', ArrayValidationException::class);
198
        Assert::keyExists($data, 'location', ArrayValidationException::class);
199
        Assert::keyExists($data, 'index', ArrayValidationException::class);
200
201
        // Make sure the items have the correct data type
202
        Assert::integer($data['index'], ArrayValidationException::class);
203
        Assert::string($data['binding'], ArrayValidationException::class);
204
        Assert::string($data['location'], ArrayValidationException::class);
205
206
        $retval = [
207
            'Binding' => $data['binding'],
208
            'Location' => $data['location'],
209
            'index' => $data['index'],
210
        ];
211
212
        if (array_key_exists('isdefault', $data)) {
213
            Assert::boolean($data['isdefault'], ArrayValidationException::class);
214
            $retval['isDefault'] = $data['isdefault'];
215
        }
216
217
        if (array_key_exists('responselocation', $data)) {
218
            Assert::string($data['responselocation'], ArrayValidationException::class);
219
            $retval['ResponseLocation'] = $data['responselocation'];
220
        }
221
222
        if (array_key_exists('children', $data)) {
223
            Assert::isArray($data['children'], ArrayValidationException::class);
224
            Assert::allIsInstanceOf(
225
                $data['children'],
226
                SerializableElementInterface::class,
227
                ArrayValidationException::class,
228
            );
229
            $retval['children'] = $data['children'];
230
        }
231
232
        if (array_key_exists('attributes', $data)) {
233
            Assert::isArray($data['attributes'], ArrayValidationException::class);
234
            Assert::allIsArray($data['attributes'], ArrayValidationException::class);
235
            foreach ($data['attributes'] as $i => $attr) {
236
                $retval['attributes'][] = XMLAttribute::fromArray($attr);
237
            }
238
        }
239
240
        return $retval;
241
    }
242
243
244
    /**
245
     * Create an array from this class
246
     *
247
     * @return array{
0 ignored issues
show
Documentation Bug introduced by
The doc comment array{ at position 2 could not be parsed: the token is null at position 2.
Loading history...
248
     *   'index': int,
249
     *   'Binding': string,
250
     *   'Location': string,
251
     *   'isDefault'?: bool,
252
     *   'ResponseLocation'?: string,
253
     *   'children'?: array,
254
     *   'attributes'?: array,
255
     * }
256
     */
257
    public function toArray(): array
258
    {
259
        $data = parent::toArray();
260
        $data['index'] = $this->getIndex()->toInteger();
261
        $data['isDefault'] = $this->getIsDefault()?->toBoolean();
262
263
        return array_filter($data);
264
    }
265
}
266