Schema::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 34
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 15
nc 1
nop 11
dl 0
loc 34
rs 9.7666
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\XMLSchema\XML;
6
7
use DOMElement;
8
use SimpleSAML\XML\Assert\Assert;
9
use SimpleSAML\XML\Constants as C_XML;
10
use SimpleSAML\XML\SchemaValidatableElementInterface;
11
use SimpleSAML\XML\SchemaValidatableElementTrait;
12
use SimpleSAML\XML\Type\LangValue;
13
use SimpleSAML\XMLSchema\Constants as C_XS;
14
use SimpleSAML\XMLSchema\Exception\InvalidDOMElementException;
15
use SimpleSAML\XMLSchema\Exception\SchemaViolationException;
16
use SimpleSAML\XMLSchema\Type\AnyURIValue;
17
use SimpleSAML\XMLSchema\Type\IDValue;
18
use SimpleSAML\XMLSchema\Type\Schema\BlockSetValue;
19
use SimpleSAML\XMLSchema\Type\Schema\FormChoiceValue;
20
use SimpleSAML\XMLSchema\Type\Schema\FullDerivationSetValue;
21
use SimpleSAML\XMLSchema\Type\TokenValue;
22
use SimpleSAML\XMLSchema\XML\Interface\RedefinableInterface;
23
use SimpleSAML\XPath\XPath;
24
25
use function array_merge;
26
use function strval;
27
28
/**
29
 * Class representing the schema-element
30
 *
31
 * @package simplesamlphp/xml-common
32
 */
33
final class Schema extends AbstractOpenAttrs implements SchemaValidatableElementInterface
34
{
35
    use SchemaValidatableElementTrait;
36
37
38
    /** @var string */
39
    public const LOCALNAME = 'schema';
40
41
    /** The exclusions for the xs:anyAttribute element */
42
    public const XS_ANY_ATTR_EXCLUSIONS = [
43
        [C_XML::NS_XML, 'lang'],
44
    ];
45
46
47
    /**
48
     * Schema constructor
49
     *
50
     * @param (
51
     *     \SimpleSAML\XMLSchema\XML\XsInclude|
52
     *     \SimpleSAML\XMLSchema\XML\Import|
53
     *     \SimpleSAML\XMLSchema\XML\Redefine|
54
     *     \SimpleSAML\XMLSchema\XML\Annotation
55
     * )[] $topLevelElements
56
     * @param (
57
     *     \SimpleSAML\XMLSchema\XML\Interface\RedefinableInterface|
58
     *     \SimpleSAML\XMLSchema\XML\TopLevelAttribute|
59
     *     \SimpleSAML\XMLSchema\XML\TopLevelElement|
60
     *     \SimpleSAML\XMLSchema\XML\Notation|
61
     *     \SimpleSAML\XMLSchema\XML\Annotation
62
     * )[] $schemaTopElements
63
     * @param \SimpleSAML\XMLSchema\Type\AnyURIValue $targetNamespace
64
     * @param \SimpleSAML\XMLSchema\Type\TokenValue $version
65
     * @param \SimpleSAML\XMLSchema\Type\Schema\FullDerivationSetValue $finalDefault
66
     * @param \SimpleSAML\XMLSchema\Type\Schema\BlockSetValue $blockDefault
67
     * @param \SimpleSAML\XMLSchema\Type\Schema\FormChoiceValue|null $attributeFormDefault
68
     * @param \SimpleSAML\XMLSchema\Type\Schema\FormChoiceValue|null $elementFormDefault
69
     * @param \SimpleSAML\XMLSchema\Type\IDValue|null $id
70
     * @param \SimpleSAML\XML\Type\LangValue|null $lang
71
     * @param array<\SimpleSAML\XML\Attribute> $namespacedAttributes
72
     */
73
    public function __construct(
74
        protected array $topLevelElements = [],
75
        protected array $schemaTopElements = [],
76
        protected ?AnyURIValue $targetNamespace = null,
77
        protected ?TokenValue $version = null,
78
        protected ?FullDerivationSetValue $finalDefault = null,
79
        protected ?BlockSetValue $blockDefault = null,
80
        protected ?FormChoiceValue $attributeFormDefault = null,
81
        protected ?FormChoiceValue $elementFormDefault = null,
82
        protected ?IDValue $id = null,
83
        protected ?LangValue $lang = null,
84
        array $namespacedAttributes = [],
85
    ) {
86
        Assert::maxCount($topLevelElements, C_XML::UNBOUNDED_LIMIT);
87
        Assert::allIsInstanceOfAny(
88
            $topLevelElements,
89
            [XsInclude::class, Import::class, Redefine::class, Annotation::class],
90
            SchemaViolationException::class,
91
        );
92
93
        Assert::maxCount($schemaTopElements, C_XML::UNBOUNDED_LIMIT);
94
        Assert::allIsInstanceOfAny(
95
            $schemaTopElements,
96
            [
97
                RedefinableInterface::class,
98
                TopLevelAttribute::class,
99
                TopLevelElement::class,
100
                Notation::class,
101
                Annotation::class,
102
            ],
103
            SchemaViolationException::class,
104
        );
105
106
        parent::__construct($namespacedAttributes);
107
    }
108
109
110
    /**
111
     * Collect the value of the topLevelElements-property
112
     *
113
     * @return (
114
     *     \SimpleSAML\XMLSchema\XML\XsInclude|
115
     *     \SimpleSAML\XMLSchema\XML\Import|
116
     *     \SimpleSAML\XMLSchema\XML\Redefine|
117
     *     \SimpleSAML\XMLSchema\XML\Annotation
118
     * )[]
119
     */
120
    public function getTopLevelElements(): array
121
    {
122
        return $this->topLevelElements;
123
    }
124
125
126
    /**
127
     * Collect the value of the schemaTopElements-property
128
     *
129
     * @return (
130
     *     \SimpleSAML\XMLSchema\XML\Interface\RedefinableInterface|
131
     *     \SimpleSAML\XMLSchema\XML\TopLevelAttribute|
132
     *     \SimpleSAML\XMLSchema\XML\TopLevelElement|
133
     *     \SimpleSAML\XMLSchema\XML\Notation|
134
     *     \SimpleSAML\XMLSchema\XML\Annotation
135
     * )[]
136
     */
137
    public function getSchemaTopElements(): array
138
    {
139
        return $this->schemaTopElements;
140
    }
141
142
143
    /**
144
     * Collect the value of the targetNamespace-property
145
     *
146
     * @return \SimpleSAML\XMLSchema\Type\AnyURIValue|null
147
     */
148
    public function getTargetNamespace(): ?AnyURIValue
149
    {
150
        return $this->targetNamespace;
151
    }
152
153
154
    /**
155
     * Collect the value of the version-property
156
     *
157
     * @return \SimpleSAML\XMLSchema\Type\TokenValue|null
158
     */
159
    public function getVersion(): ?TokenValue
160
    {
161
        return $this->version;
162
    }
163
164
165
    /**
166
     * Collect the value of the blockDefault-property
167
     *
168
     * @return \SimpleSAML\XMLSchema\Type\Schema\BlockSetValue|null
169
     */
170
    public function getBlockDefault(): ?BlockSetValue
171
    {
172
        return $this->blockDefault;
173
    }
174
175
176
    /**
177
     * Collect the value of the finalDefault-property
178
     *
179
     * @return \SimpleSAML\XMLSchema\Type\Schema\FullDerivationSetValue|null
180
     */
181
    public function getFinalDefault(): ?FullDerivationSetValue
182
    {
183
        return $this->finalDefault;
184
    }
185
186
187
    /**
188
     * Collect the value of the attributeFormDefault-property
189
     *
190
     * @return \SimpleSAML\XMLSchema\Type\Schema\FormChoiceValue|null
191
     */
192
    public function getAttributeFormDefault(): ?FormChoiceValue
193
    {
194
        return $this->attributeFormDefault;
195
    }
196
197
198
    /**
199
     * Collect the value of the elementFormDefault-property
200
     *
201
     * @return \SimpleSAML\XMLSchema\Type\Schema\FormChoiceValue|null
202
     */
203
    public function getElementFormDefault(): ?FormChoiceValue
204
    {
205
        return $this->elementFormDefault;
206
    }
207
208
209
    /**
210
     * Collect the value of the id-property
211
     *
212
     * @return \SimpleSAML\XMLSchema\Type\IDValue|null
213
     */
214
    public function getID(): ?IDValue
215
    {
216
        return $this->id;
217
    }
218
219
220
    /**
221
     * Collect the value of the lang-property
222
     *
223
     * @return \SimpleSAML\XML\Type\LangValue|null
224
     */
225
    public function getLang(): ?LangValue
226
    {
227
        return $this->lang;
228
    }
229
230
231
    /**
232
     * Test if an object, at the state it's in, would produce an empty XML-element
233
     *
234
     * @return bool
235
     */
236
    public function isEmptyElement(): bool
237
    {
238
        return parent::isEmptyElement() &&
239
            empty($this->getTopLevelElements()) &&
240
            empty($this->getSchemaTopElements()) &&
241
            empty($this->getTargetNamespace()) &&
242
            empty($this->getVersion()) &&
243
            empty($this->getFinalDefault()) &&
244
            empty($this->getBlockDefault()) &&
245
            empty($this->getAttributeFormDefault()) &&
246
            empty($this->getElementFormDefault()) &&
247
            empty($this->getId()) &&
248
            empty($this->getLang());
249
    }
250
251
252
    /**
253
     * Create an instance of this object from its XML representation.
254
     *
255
     * @param \DOMElement $xml
256
     * @return static
257
     *
258
     * @throws \SimpleSAML\XMLSchema\Exception\InvalidDOMElementException
259
     *   if the qualified name of the supplied element is wrong
260
     */
261
    public static function fromXML(DOMElement $xml): static
262
    {
263
        Assert::same($xml->localName, static::getLocalName(), InvalidDOMElementException::class);
264
        Assert::same($xml->namespaceURI, static::NS, InvalidDOMElementException::class);
265
266
        $xpCache = XPath::getXPath($xml);
267
268
        $beforeAllowed = [
269
            'annotation' => Annotation::class,
270
            'import' => Import::class,
271
            'include' => XsInclude::class,
272
            'redefine' => Redefine::class,
273
        ];
274
        $beforeSchemaTopElements = XPath::xpQuery(
275
            $xml,
276
            '('
277
            . '/xs:schema/xs:group|'
278
            . '/xs:schema/xs:attributeGroup|'
279
            . '/xs:schema/xs:complexType|'
280
            . '/xs:schema/xs:simpleType|'
281
            . '/xs:schema/xs:element|'
282
            .  '/xs:schema/xs:attribute'
283
            . ')[1]/preceding-sibling::xs:*',
284
            $xpCache,
285
        );
286
287
        $topLevelElements = [];
288
        foreach ($beforeSchemaTopElements as $node) {
289
            if ($node instanceof DOMElement) {
290
                if ($node->namespaceURI === C_XS::NS_XS && array_key_exists($node->localName, $beforeAllowed)) {
291
                    $topLevelElements[] = $beforeAllowed[$node->localName]::fromXML($node);
292
                }
293
            }
294
        }
295
296
        $afterAllowed = [
297
            'annotation' => Annotation::class,
298
            'attribute' => TopLevelAttribute::class,
299
            'attributeGroup' => NamedAttributeGroup::class,
300
            'complexType' => TopLevelComplexType::class,
301
            'element' => TopLevelElement::class,
302
            'notation' => Notation::class,
303
            'simpleType' => TopLevelSimpleType::class,
304
        ];
305
        $afterSchemaTopElementFirstHit = XPath::xpQuery(
306
            $xml,
307
            '('
308
            . '/xs:schema/xs:group|'
309
            . '/xs:schema/xs:attributeGroup|'
310
            . '/xs:schema/xs:complexType'
311
            . '/xs:schema/xs:simpleType|'
312
            . '/xs:schema/xs:element|'
313
            . '/xs:schema/xs:attribute'
314
            . ')[1]',
315
            $xpCache,
316
        );
317
318
        $afterSchemaTopElementSibling = XPath::xpQuery(
319
            $xml,
320
            '('
321
            . '/xs:schema/xs:group|'
322
            . '/xs:schema/xs:attributeGroup|'
323
            . '/xs:schema/xs:complexType'
324
            . '/xs:schema/xs:simpleType|'
325
            . '/xs:schema/xs:element|'
326
            . '/xs:schema/xs:attribute'
327
            . ')[1]/following-sibling::xs:*',
328
            $xpCache,
329
        );
330
331
        $afterSchemaTopElements = array_merge($afterSchemaTopElementFirstHit, $afterSchemaTopElementSibling);
332
333
        $schemaTopElements = [];
334
        foreach ($afterSchemaTopElements as $node) {
335
            if ($node instanceof DOMElement) {
336
                if ($node->namespaceURI === C_XS::NS_XS && array_key_exists($node->localName, $afterAllowed)) {
337
                    $schemaTopElements[] = $afterAllowed[$node->localName]::fromXML($node);
338
                }
339
            }
340
        }
341
342
        $lang = $xml->hasAttributeNS(C_XML::NS_XML, 'lang')
343
            ? LangValue::fromString($xml->getAttributeNS(C_XML::NS_XML, 'lang'))
344
            : null;
345
346
        return new static(
347
            $topLevelElements,
348
            $schemaTopElements,
349
            self::getOptionalAttribute($xml, 'targetNamespace', AnyURIValue::class, null),
350
            self::getOptionalAttribute($xml, 'version', TokenValue::class, null),
351
            self::getOptionalAttribute($xml, 'finalDefault', FullDerivationSetValue::class, null),
352
            self::getOptionalAttribute($xml, 'blockDefault', BlockSetValue::class, null),
353
            self::getOptionalAttribute($xml, 'attributeFormDefault', FormChoiceValue::class, null),
354
            self::getOptionalAttribute($xml, 'elementFormDefault', FormChoiceValue::class, null),
355
            self::getOptionalAttribute($xml, 'id', IDValue::class, null),
356
            $lang,
357
            self::getAttributesNSFromXML($xml),
358
        );
359
    }
360
361
362
    /**
363
     * Add this Schema to an XML element.
364
     *
365
     * @param \DOMElement|null $parent The element we should append this Schema to.
366
     * @return \DOMElement
367
     */
368
    public function toXML(?DOMElement $parent = null): DOMElement
369
    {
370
        $e = parent::toXML($parent);
371
372
        if ($this->getTargetNamespace() !== null) {
373
            $e->setAttribute('targetNamespace', strval($this->getTargetNamespace()));
374
        }
375
376
        if ($this->getVersion() !== null) {
377
            $e->setAttribute('version', strval($this->getVersion()));
378
        }
379
380
        if ($this->getFinalDefault() !== null) {
381
            $e->setAttribute('finalDefault', strval($this->getFinalDefault()));
382
        }
383
384
        if ($this->getBlockDefault() !== null) {
385
            $e->setAttribute('blockDefault', strval($this->getBlockDefault()));
386
        }
387
388
        if ($this->getAttributeFormDefault() !== null) {
389
            $e->setAttribute('attributeFormDefault', strval($this->getAttributeFormDefault()));
390
        }
391
392
        if ($this->getElementFormDefault() !== null) {
393
            $e->setAttribute('elementFormDefault', strval($this->getElementFormDefault()));
394
        }
395
396
        if ($this->getId() !== null) {
397
            $e->setAttribute('id', strval($this->getId()));
398
        }
399
400
        $this->getLang()?->toAttribute()->toXML($e);
401
402
        foreach ($this->getTopLevelElements() as $tle) {
403
            $tle->toXML($e);
404
        }
405
406
        foreach ($this->getSchemaTopElements() as $ste) {
407
            $ste->toXML($e);
408
        }
409
410
        return $e;
411
    }
412
}
413