Passed
Push — master ( 66592a...152b2f )
by Tim
02:08
created

UIInfo::fromArray()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

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