DiscoHints   A
last analyzed

Complexity

Total Complexity 29

Size/Duplication

Total Lines 263
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 88
dl 0
loc 263
rs 10
c 0
b 0
f 0
wmc 29

11 Methods

Rating   Name   Duplication   Size   Complexity  
A addChild() 0 3 1
A fromArray() 0 9 1
A getIPHint() 0 3 1
A toXML() 0 22 5
A getDomainHint() 0 3 1
A fromXML() 0 17 2
A getGeolocationHint() 0 3 1
A isEmptyElement() 0 6 4
A __construct() 0 14 1
A toArray() 0 22 4
B processArrayContents() 0 53 8
1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\SAML2\XML\mdui;
6
7
use DOMElement;
8
use SimpleSAML\Assert\Assert;
9
use SimpleSAML\SAML2\Exception\ArrayValidationException;
10
use SimpleSAML\SAML2\Utils\XPath;
11
use SimpleSAML\XML\ArrayizableElementInterface;
12
use SimpleSAML\XML\Chunk;
13
use SimpleSAML\XML\Constants as C;
14
use SimpleSAML\XML\Exception\InvalidDOMElementException;
15
use SimpleSAML\XML\ExtendableElementTrait;
16
use SimpleSAML\XML\SchemaValidatableElementInterface;
17
use SimpleSAML\XML\SchemaValidatableElementTrait;
18
use SimpleSAML\XML\SerializableElementInterface;
19
use SimpleSAML\XML\XsNamespace as NS;
20
21
use function array_filter;
22
use function array_key_exists;
23
use function array_keys;
24
25
/**
26
 * Class for handling the metadata extensions for login and discovery user interface
27
 *
28
 * @link: http://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-metadata-ui/v1.0/sstc-saml-metadata-ui-v1.0.pdf
29
 * @package simplesamlphp/saml2
30
 */
31
final class DiscoHints extends AbstractMduiElement implements
32
    ArrayizableElementInterface,
33
    SchemaValidatableElementInterface
34
{
35
    use ExtendableElementTrait;
36
    use SchemaValidatableElementTrait;
37
38
    /** The namespace-attribute for the xs:any element */
39
    public const XS_ANY_ELT_NAMESPACE = NS::OTHER;
40
41
42
    /**
43
     * Create a DiscoHints element.
44
     *
45
     * @param \SimpleSAML\XML\Chunk[] $children
46
     * @param \SimpleSAML\SAML2\XML\mdui\IPHint[] $ipHint
47
     * @param \SimpleSAML\SAML2\XML\mdui\DomainHint[] $domainHint
48
     * @param \SimpleSAML\SAML2\XML\mdui\GeolocationHint[] $geolocationHint
49
     */
50
    public function __construct(
51
        array $children = [],
52
        protected array $ipHint = [],
53
        protected array $domainHint = [],
54
        protected array $geolocationHint = [],
55
    ) {
56
        Assert::maxCount($ipHint, C::UNBOUNDED_LIMIT);
57
        Assert::maxCount($domainHint, C::UNBOUNDED_LIMIT);
58
        Assert::maxCount($geolocationHint, C::UNBOUNDED_LIMIT);
59
        Assert::allIsInstanceOf($ipHint, IPHint::class);
60
        Assert::allIsInstanceOf($domainHint, DomainHint::class);
61
        Assert::allIsInstanceOf($geolocationHint, GeolocationHint::class);
62
63
        $this->setElements($children);
64
    }
65
66
67
    /**
68
     * Collect the value of the IPHint-property
69
     *
70
     * @return \SimpleSAML\SAML2\XML\mdui\IPHint[]
71
     */
72
    public function getIPHint(): array
73
    {
74
        return $this->ipHint;
75
    }
76
77
78
    /**
79
     * Collect the value of the DomainHint-property
80
     *
81
     * @return \SimpleSAML\SAML2\XML\mdui\DomainHint[]
82
     */
83
    public function getDomainHint(): array
84
    {
85
        return $this->domainHint;
86
    }
87
88
89
    /**
90
     * Collect the value of the GeolocationHint-property
91
     *
92
     * @return \SimpleSAML\SAML2\XML\mdui\GeolocationHint[]
93
     */
94
    public function getGeolocationHint(): array
95
    {
96
        return $this->geolocationHint;
97
    }
98
99
100
    /**
101
     * Add the value to the elements-property
102
     *
103
     * @param \SimpleSAML\XML\Chunk $child
104
     */
105
    public function addChild(Chunk $child): void
106
    {
107
        $this->elements[] = $child;
108
    }
109
110
111
    /**
112
     * Test if an object, at the state it's in, would produce an empty XML-element
113
     *
114
     * @return bool
115
     */
116
    public function isEmptyElement(): bool
117
    {
118
        return empty($this->elements)
119
            && empty($this->ipHint)
120
            && empty($this->domainHint)
121
            && empty($this->geolocationHint);
122
    }
123
124
125
    /**
126
     * Convert XML into a DiscoHints
127
     *
128
     * @param \DOMElement $xml The XML element we should load
129
     * @return static
130
     *
131
     * @throws \SimpleSAML\XML\Exception\InvalidDOMElementException
132
     *   if the qualified name of the supplied element is wrong
133
     */
134
    public static function fromXML(DOMElement $xml): static
135
    {
136
        Assert::same($xml->localName, 'DiscoHints', InvalidDOMElementException::class);
137
        Assert::same($xml->namespaceURI, DiscoHints::NS, InvalidDOMElementException::class);
138
139
        $IPHint = IPHint::getChildrenOfClass($xml);
140
        $DomainHint = DomainHint::getChildrenOfClass($xml);
141
        $GeolocationHint = GeolocationHint::getChildrenOfClass($xml);
142
        $children = [];
143
144
        /** @var \DOMElement[] $nodes */
145
        $nodes = XPath::xpQuery($xml, "./*[namespace-uri()!='" . DiscoHints::NS . "']", XPath::getXPath($xml));
146
        foreach ($nodes as $node) {
147
            $children[] = new Chunk($node);
148
        }
149
150
        return new static($children, $IPHint, $DomainHint, $GeolocationHint);
151
    }
152
153
154
    /**
155
     * Convert this DiscoHints to XML.
156
     *
157
     * @param \DOMElement|null $parent The element we should append to.
158
     * @return \DOMElement
159
     */
160
    public function toXML(?DOMElement $parent = null): DOMElement
161
    {
162
        $e = $this->instantiateParentElement($parent);
163
164
        foreach ($this->getIPHint() as $hint) {
165
            $hint->toXML($e);
166
        }
167
168
        foreach ($this->getDomainHint() as $hint) {
169
            $hint->toXML($e);
170
        }
171
172
        foreach ($this->getGeolocationHint() as $hint) {
173
            $hint->toXML($e);
174
        }
175
176
        /** @var \SimpleSAML\XML\SerializableElementInterface $child */
177
        foreach ($this->getElements() as $child) {
178
            $child->toXML($e);
179
        }
180
181
        return $e;
182
    }
183
184
185
    /**
186
     * Create a class from an array
187
     *
188
     * @param array $data
189
     * @return static
190
     */
191
    public static function fromArray(array $data): static
192
    {
193
        $data = self::processArrayContents($data);
194
195
        return new static(
196
            $data['children'] ?? [],
197
            $data['IPHint'] ?? [],
198
            $data['DomainHint'] ?? [],
199
            $data['GeolocationHint'] ?? [],
200
        );
201
    }
202
203
204
    /**
205
     * Validates an array representation of this object and returns the same array with
206
     * rationalized keys (casing) and parsed sub-elements.
207
     *
208
     * @param array $data
209
     * @return array $data
210
     */
211
    private static function processArrayContents(array $data): array
212
    {
213
        $data = array_change_key_case($data, CASE_LOWER);
214
215
        // Make sure the array keys are known for this kind of object
216
        Assert::allOneOf(
217
            array_keys($data),
218
            [
219
                'iphint',
220
                'domainhint',
221
                'geolocationhint',
222
                'children',
223
            ],
224
            ArrayValidationException::class,
225
        );
226
227
        $retval = [];
228
229
        if (array_key_exists('iphint', $data)) {
230
            Assert::isArray($data['iphint'], ArrayValidationException::class);
231
            Assert::allString($data['iphint'], ArrayValidationException::class);
232
            foreach ($data['iphint'] as $hint) {
233
                $retval['IPHint'][] = new IPHint($hint);
234
            }
235
        }
236
237
        if (array_key_exists('domainhint', $data)) {
238
            Assert::isArray($data['domainhint'], ArrayValidationException::class);
239
            Assert::allString($data['domainhint'], ArrayValidationException::class);
240
            foreach ($data['domainhint'] as $hint) {
241
                $retval['DomainHint'][] = new DomainHint($hint);
242
            }
243
        }
244
245
        if (array_key_exists('geolocationhint', $data)) {
246
            Assert::isArray($data['geolocationhint'], ArrayValidationException::class);
247
            Assert::allString($data['geolocationhint'], ArrayValidationException::class);
248
            foreach ($data['geolocationhint'] as $hint) {
249
                $retval['GeolocationHint'][] = new GeolocationHint($hint);
250
            }
251
        }
252
253
        if (array_key_exists('children', $data)) {
254
            Assert::isArray($data['children'], ArrayValidationException::class);
255
            Assert::allIsInstanceOf(
256
                $data['children'],
257
                SerializableElementInterface::class,
258
                ArrayValidationException::class,
259
            );
260
            $retval['children'] = $data['children'];
261
        }
262
263
        return $retval;
264
    }
265
266
267
    /**
268
     * Create an array from this class
269
     *
270
     * @return array
271
     */
272
    public function toArray(): array
273
    {
274
        $data = [
275
            'IPHint' => [],
276
            'DomainHint' => [],
277
            'GeolocationHint' => [],
278
            'children' => $this->getElements(),
279
        ];
280
281
        foreach ($this->getIPHint() as $hint) {
282
            $data['IPHint'][] = $hint->getContent();
283
        }
284
285
        foreach ($this->getDomainHint() as $hint) {
286
            $data['DomainHint'][] = $hint->getContent();
287
        }
288
289
        foreach ($this->getGeolocationHint() as $hint) {
290
            $data['GeolocationHint'][] = $hint->getContent();
291
        }
292
293
        return array_filter($data);
294
    }
295
}
296