Passed
Push — master ( e1b24a...13407a )
by Tim
02:42
created

UIInfo   C

Complexity

Total Complexity 56

Size/Duplication

Total Lines 411
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 136
dl 0
loc 411
rs 5.5199
c 2
b 0
f 0
wmc 56

16 Methods

Rating   Name   Duplication   Size   Complexity  
A getDescription() 0 3 1
A getInformationURL() 0 3 1
A addKeyword() 0 9 1
A getLogo() 0 3 1
A getDisplayName() 0 3 1
A getPrivacyStatementURL() 0 3 1
A getKeywords() 0 3 1
A addLogo() 0 3 1
A addChild() 0 3 1
A __construct() 0 47 1
A fromXML() 0 28 3
B isEmptyElement() 0 9 7
C fromArray() 0 51 13
A testLocalizedElements() 0 17 2
B toXML() 0 33 8
F toArray() 0 39 13

How to fix   Complexity   

Complex Class

Complex classes like UIInfo often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use UIInfo, and based on these observations, apply Extract Interface, too.

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