Completed
Push — master ( f72cfb...4ef12f )
by Tim
21s queued 18s
created

Schema::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 31
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

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