UIInfo::toArray()   F
last analyzed

Complexity

Conditions 14
Paths 8192

Size

Total Lines 42
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 14
eloc 27
nc 8192
nop 0
dl 0
loc 42
rs 2.1
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\Exception\ProtocolViolationException;
11
use SimpleSAML\SAML2\Utils\XPath;
12
use SimpleSAML\XML\ArrayizableElementInterface;
13
use SimpleSAML\XML\Chunk;
14
use SimpleSAML\XML\Constants as C;
15
use SimpleSAML\XML\Exception\InvalidDOMElementException;
16
use SimpleSAML\XML\ExtendableElementTrait;
17
use SimpleSAML\XML\SchemaValidatableElementInterface;
18
use SimpleSAML\XML\SchemaValidatableElementTrait;
19
use SimpleSAML\XML\SerializableElementInterface;
20
use SimpleSAML\XML\XsNamespace as NS;
21
22
use function array_filter;
23
use function array_key_exists;
24
use function array_keys;
25
use function array_map;
26
use function array_merge;
27
use function array_unique;
28
29
/**
30
 * Class for handling the metadata extensions for login and discovery user interface
31
 *
32
 * @link: http://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-metadata-ui/v1.0/sstc-saml-metadata-ui-v1.0.pdf
33
 * @package simplesamlphp/saml2
34
 */
35
final class UIInfo extends AbstractMduiElement implements
36
    ArrayizableElementInterface,
37
    SchemaValidatableElementInterface
38
{
39
    use ExtendableElementTrait;
40
    use SchemaValidatableElementTrait;
41
42
    /** The namespace-attribute for the xs:any element */
43
    public const XS_ANY_ELT_NAMESPACE = NS::OTHER;
44
45
    /**
46
     * Create a UIInfo element.
47
     *
48
     * @param \SimpleSAML\SAML2\XML\mdui\DisplayName[] $displayName
49
     * @param \SimpleSAML\SAML2\XML\mdui\Description[] $description
50
     * @param \SimpleSAML\SAML2\XML\mdui\InformationURL[] $informationURL
51
     * @param \SimpleSAML\SAML2\XML\mdui\PrivacyStatementURL[] $privacyStatementURL
52
     * @param \SimpleSAML\SAML2\XML\mdui\Keywords[] $keywords
53
     * @param \SimpleSAML\SAML2\XML\mdui\Logo[] $logo
54
     * @param \SimpleSAML\XML\Chunk[] $children
55
     */
56
    public function __construct(
57
        protected array $displayName = [],
58
        protected array $description = [],
59
        protected array $informationURL = [],
60
        protected array $privacyStatementURL = [],
61
        protected array $keywords = [],
62
        protected array $logo = [],
63
        array $children = [],
64
    ) {
65
        Assert::maxCount($displayName, C::UNBOUNDED_LIMIT);
66
        Assert::allIsInstanceOf($displayName, DisplayName::class);
67
        /**
68
         * 2.1.2:  There MUST NOT be more than one <mdui:DisplayName>,
69
         *         within a given <mdui:UIInfo>, for a given language
70
         */
71
        $this->testLocalizedElements($displayName);
72
73
        Assert::maxCount($description, C::UNBOUNDED_LIMIT);
74
        Assert::allIsInstanceOf($description, Description::class);
75
        /**
76
         * 2.1.3:  There MUST NOT be more than one <mdui:Description>,
77
         *         within a given <mdui:UIInfo>, for a given language
78
         */
79
        $this->testLocalizedElements($description);
80
81
        Assert::maxCount($keywords, C::UNBOUNDED_LIMIT);
82
        Assert::allIsInstanceOf($keywords, Keywords::class);
83
        /**
84
         * 2.1.4:  There MUST NOT be more than one <mdui:Keywords>,
85
         *         within a given <mdui:UIInfo>, for a given language
86
         */
87
        $this->testLocalizedElements($keywords);
88
89
        Assert::maxCount($informationURL, C::UNBOUNDED_LIMIT);
90
        Assert::allIsInstanceOf($informationURL, InformationURL::class);
91
        /**
92
         * 2.1.6:  There MUST NOT be more than one <mdui:InformationURL>,
93
         *         within a given <mdui:UIInfo>, for a given language
94
         */
95
        $this->testLocalizedElements($informationURL);
96
97
        Assert::maxCount($privacyStatementURL, C::UNBOUNDED_LIMIT);
98
        Assert::allIsInstanceOf($privacyStatementURL, PrivacyStatementURL::class);
99
        /**
100
         * 2.1.7:  There MUST NOT be more than one <mdui:PrivacyStatementURL>,
101
         *         within a given <mdui:UIInfo>, for a given language
102
         */
103
        $this->testLocalizedElements($privacyStatementURL);
104
105
        Assert::maxCount($logo, C::UNBOUNDED_LIMIT);
106
        Assert::allIsInstanceOf($logo, Logo::class);
107
108
        $this->setElements($children);
109
    }
110
111
112
    /**
113
     * Collect the value of the Keywords-property
114
     *
115
     * @return \SimpleSAML\SAML2\XML\mdui\Keywords[]
116
     */
117
    public function getKeywords(): array
118
    {
119
        return $this->keywords;
120
    }
121
122
123
    /**
124
     * Add the value to the Keywords-property
125
     *
126
     * @param \SimpleSAML\SAML2\XML\mdui\Keywords $keyword
127
     */
128
    public function addKeyword(Keywords $keyword): void
129
    {
130
        /**
131
         * 2.1.4:  There MUST NOT be more than one <mdui:Keywords>,
132
         *         within a given <mdui:UIInfo>, for a given language
133
         */
134
        $keywords = array_merge($this->keywords, [$keyword]);
135
        $this->testLocalizedElements($keywords);
136
        $this->keywords = $keywords;
137
    }
138
139
140
    /**
141
     * Collect the value of the DisplayName-property
142
     *
143
     * @return \SimpleSAML\SAML2\XML\mdui\DisplayName[]
144
     */
145
    public function getDisplayName(): array
146
    {
147
        return $this->displayName;
148
    }
149
150
151
    /**
152
     * Collect the value of the Description-property
153
     *
154
     * @return \SimpleSAML\SAML2\XML\mdui\Description[]
155
     */
156
    public function getDescription(): array
157
    {
158
        return $this->description;
159
    }
160
161
162
    /**
163
     * Collect the value of the InformationURL-property
164
     * @return \SimpleSAML\SAML2\XML\mdui\InformationURL[]
165
     */
166
    public function getInformationURL(): array
167
    {
168
        return $this->informationURL;
169
    }
170
171
172
    /**
173
     * Collect the value of the PrivacyStatementURL-property
174
     *
175
     * @return \SimpleSAML\SAML2\XML\mdui\PrivacyStatementURL[]
176
     */
177
    public function getPrivacyStatementURL(): array
178
    {
179
        return $this->privacyStatementURL;
180
    }
181
182
183
    /**
184
     * Collect the value of the Logo-property
185
     *
186
     * @return \SimpleSAML\SAML2\XML\mdui\Logo[]
187
     */
188
    public function getLogo(): array
189
    {
190
        return $this->logo;
191
    }
192
193
194
    /**
195
     * Add the value to the Logo-property
196
     *
197
     * @param \SimpleSAML\SAML2\XML\mdui\Logo $logo
198
     */
199
    public function addLogo(Logo $logo): void
200
    {
201
        $this->logo[] = $logo;
202
    }
203
204
205
    /**
206
     * Add the value to the elements-property
207
     *
208
     * @param \SimpleSAML\XML\Chunk $child
209
     */
210
    public function addChild(Chunk $child): void
211
    {
212
        $this->elements[] = $child;
213
    }
214
215
216
    /**
217
     * Test if an object, at the state it's in, would produce an empty XML-element
218
     *
219
     * @return bool
220
     */
221
    public function isEmptyElement(): bool
222
    {
223
        return empty($this->displayName)
224
            && empty($this->description)
225
            && empty($this->informationURL)
226
            && empty($this->privacyStatementURL)
227
            && empty($this->keywords)
228
            && empty($this->logo)
229
            && empty($this->elements);
230
    }
231
232
233
    /**
234
     * Test localized elements for multiple items with the same language
235
     *
236
     * @param (\SimpleSAML\SAML2\XML\md\AbstractLocalizedURL|
237
     *         \SimpleSAML\SAML2\XML\md\AbstractLocalizedName|
238
     *         \SimpleSAML\SAML2\XML\mdui\Keywords)[] $elements
239
     * @return void
240
     */
241
    private function testLocalizedElements(array $elements)
242
    {
243
        if (!empty($elements)) {
244
            $types = array_map('get_class', $elements);
245
            Assert::maxCount(array_unique($types), 1, 'Multiple class types cannot be used.');
246
247
            $languages = array_map(
248
                function ($elt) {
249
                    return $elt->getLanguage();
250
                },
251
                $elements,
252
            );
253
            Assert::uniqueValues(
254
                $languages,
255
                'There MUST NOT be more than one <' . $elements[0]->getQualifiedName() . '>,'
256
                . ' within a given <mdui:UIInfo>, for a given language',
257
                ProtocolViolationException::class,
258
            );
259
        }
260
    }
261
262
263
    /**
264
     * Convert XML into a UIInfo
265
     *
266
     * @param \DOMElement $xml The XML element we should load
267
     * @return static
268
     *
269
     * @throws \SimpleSAML\XML\Exception\InvalidDOMElementException
270
     *   if the qualified name of the supplied element is wrong
271
     */
272
    public static function fromXML(DOMElement $xml): static
273
    {
274
        Assert::same($xml->localName, 'UIInfo', InvalidDOMElementException::class);
275
        Assert::same($xml->namespaceURI, UIInfo::NS, InvalidDOMElementException::class);
276
277
        $DisplayName = DisplayName::getChildrenOfClass($xml);
278
        $Description = Description::getChildrenOfClass($xml);
279
        $InformationURL = InformationURL::getChildrenOfClass($xml);
280
        $PrivacyStatementURL = PrivacyStatementURL::getChildrenOfClass($xml);
281
        $Keywords = Keywords::getChildrenOfClass($xml);
282
        $Logo = Logo::getChildrenOfClass($xml);
283
        $children = [];
284
285
        /** @var \DOMElement $node */
286
        foreach (XPath::xpQuery($xml, './*', XPath::getXPath($xml)) as $node) {
287
            if ($node->namespaceURI !== UIInfo::NS) {
288
                $children[] = new Chunk($node);
289
            }
290
        }
291
292
        return new static(
293
            $DisplayName,
294
            $Description,
295
            $InformationURL,
296
            $PrivacyStatementURL,
297
            $Keywords,
298
            $Logo,
299
            $children,
300
        );
301
    }
302
303
304
    /**
305
     * Convert this UIInfo to XML.
306
     *
307
     * @param \DOMElement|null $parent The element we should append to.
308
     * @return \DOMElement
309
     */
310
    public function toXML(?DOMElement $parent = null): DOMElement
311
    {
312
        $e = $this->instantiateParentElement($parent);
313
314
        foreach ($this->getDisplayName() as $child) {
315
            $child->toXML($e);
316
        }
317
318
        foreach ($this->getDescription() as $child) {
319
            $child->toXML($e);
320
        }
321
322
        foreach ($this->getInformationURL() as $child) {
323
            $child->toXML($e);
324
        }
325
326
        foreach ($this->getPrivacyStatementURL() as $child) {
327
            $child->toXML($e);
328
        }
329
330
        foreach ($this->getKeywords() as $child) {
331
            $child->toXML($e);
332
        }
333
334
        foreach ($this->getLogo() as $child) {
335
            $child->toXML($e);
336
        }
337
338
        /** @var \SimpleSAML\XML\SerializableElementInterface $child */
339
        foreach ($this->getElements() as $child) {
340
            $child->toXML($e);
341
        }
342
343
        return $e;
344
    }
345
346
347
    /**
348
     * Create a class from an array
349
     *
350
     * NOTE: this method does not support passing additional child-objects
351
     *
352
     * @param array $data
353
     * @return static
354
     */
355
    public static function fromArray(array $data): static
356
    {
357
        $data = self::processArrayContents($data);
358
359
        return new static(
360
            $data['DisplayName'] ?? [],
361
            $data['Description'] ?? [],
362
            $data['InformationURL'] ?? [],
363
            $data['PrivacyStatementURL'] ?? [],
364
            $data['Keywords'] ?? [],
365
            $data['Logo'] ?? [],
366
            $data['children'] ?? [],
367
        );
368
    }
369
370
371
    /**
372
     * Validates an array representation of this object and returns the same array with
373
     * rationalized keys (casing) and parsed sub-elements.
374
     *
375
     * @param array $data
376
     * @return array $data
377
     */
378
    private static function processArrayContents(array $data): array
379
    {
380
        $data = array_change_key_case($data, CASE_LOWER);
381
382
        // Make sure the array keys are known for this kind of object
383
        Assert::allOneOf(
384
            array_keys($data),
385
            [
386
                'displayname',
387
                'description',
388
                'informationurl',
389
                'privacystatementurl',
390
                'keywords',
391
                'logo',
392
                'children',
393
            ],
394
            ArrayValidationException::class,
395
        );
396
397
        $retval = [];
398
399
        if (array_key_exists('displayname', $data)) {
400
            foreach ($data['displayname'] as $l => $displayName) {
401
                $retval['DisplayName'][] = DisplayName::fromArray([$l => $displayName]);
402
            }
403
        }
404
405
        if (array_key_exists('description', $data)) {
406
            foreach ($data['description'] as $l => $description) {
407
                $retval['Description'][] = Description::fromArray([$l => $description]);
408
            }
409
        }
410
411
        if (array_key_exists('informationurl', $data)) {
412
            foreach ($data['informationurl'] as $l => $iu) {
413
                $retval['InformationURL'][] = InformationURL::fromArray([$l => $iu]);
414
            }
415
        }
416
417
        if (array_key_exists('privacystatementurl', $data)) {
418
            foreach ($data['privacystatementurl'] as $l => $psu) {
419
                $retval['PrivacyStatementURL'][] = PrivacyStatementURL::fromArray([$l => $psu]);
420
            }
421
        }
422
423
        if (array_key_exists('keywords', $data)) {
424
            foreach ($data['keywords'] as $l => $keywords) {
425
                $retval['Keywords'][] = Keywords::fromArray([$l => $keywords]);
426
            }
427
        }
428
429
        if (array_key_exists('logo', $data)) {
430
            foreach ($data['logo'] as $logo) {
431
                $retval['Logo'][] = Logo::fromArray($logo);
432
            }
433
        }
434
435
        if (array_key_exists('children', $data)) {
436
            Assert::isArray($data['children'], ArrayValidationException::class);
437
            Assert::allIsInstanceOf(
438
                $data['children'],
439
                SerializableElementInterface::class,
440
                ArrayValidationException::class,
441
            );
442
            $retval['children'] = $data['children'];
443
        }
444
445
        return array_filter($retval);
446
    }
447
448
449
    /**
450
     * Create an array from this class
451
     *
452
     * NOTE: this method does not support passing additional child-objects
453
     *
454
     * @return array
455
     */
456
    public function toArray(): array
457
    {
458
        $displayName = [];
459
        foreach ($this->getDisplayName() as $child) {
460
            $displayName = array_merge($displayName, $child->toArray());
461
        }
462
463
        $description = [];
464
        foreach ($this->getDescription() as $child) {
465
            $description = array_merge($description, $child->toArray());
466
        }
467
468
        $infoUrl = [];
469
        foreach ($this->getInformationURL() as $child) {
470
            $infoUrl = array_merge($infoUrl, $child->toArray());
471
        }
472
473
        $privacyUrl = [];
474
        foreach ($this->getPrivacyStatementURL() as $child) {
475
            $privacyUrl = array_merge($privacyUrl, $child->toArray());
476
        }
477
478
        $keywords = [];
479
        foreach ($this->getKeywords() as $child) {
480
            $keywords = array_merge($keywords, $child->toArray());
481
        }
482
483
        $logo = [];
484
        foreach ($this->getLogo() as $child) {
485
            $logo[] = $child->toArray();
486
        }
487
488
        $children = $this->getElements();
489
490
        return [] +
491
            (empty($displayName) ? [] : ['DisplayName' => $displayName]) +
492
            (empty($description) ? [] : ['Description' => $description]) +
493
            (empty($infoUrl) ? [] : ['InformationURL' => $infoUrl]) +
494
            (empty($privacyUrl) ? [] : ['PrivacyStatementURL' => $privacyUrl]) +
495
            (empty($keywords) ? [] : ['Keywords' => $keywords]) +
496
            (empty($logo) ? [] : ['Logo' => $logo]) +
497
            (empty($children) ? [] : ['children' => $children]);
498
    }
499
}
500