Passed
Push — master ( 8413b2...00f101 )
by Tim
02:19
created

Schema::__construct()   A

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