Passed
Push — master ( b40dc0...3ad4d2 )
by Tim
02:26
created

AbstractElement::getNormalization()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 6
nc 2
nop 0
dl 0
loc 10
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\XML;
6
7
use DOMElement;
8
use RuntimeException;
9
use SimpleSAML\XML\Assert\Assert;
10
use SimpleSAML\XML\SerializableElementTrait;
11
use SimpleSAML\XMLSchema\Exception\MissingAttributeException;
12
use SimpleSAML\XMLSchema\Exception\SchemaViolationException;
13
use SimpleSAML\XMLSchema\Type\Interface\ValueTypeInterface;
14
use SimpleSAML\XMLSchema\Type\QNameValue;
15
use SimpleSAML\XMLSchema\Type\StringValue;
16
17
use function array_slice;
18
use function defined;
19
use function explode;
20
use function join;
21
use function strval;
22
23
/**
24
 * Abstract class to be implemented by all classes
25
 *
26
 * @package simplesamlphp/xml-common
27
 */
28
abstract class AbstractElement implements SerializableElementInterface
29
{
30
    use SerializableElementTrait;
31
32
33
    /**
34
     * Create a document structure for this element
35
     *
36
     * @param \DOMElement|null $parent The element we should append to.
37
     * @return \DOMElement
38
     */
39
    public function instantiateParentElement(?DOMElement $parent = null): DOMElement
40
    {
41
        $qualifiedName = $this->getQualifiedName();
42
        $namespace = static::getNamespaceURI();
43
44
        if ($parent === null) {
45
            $parent = DOMDocumentFactory::create();
46
            $e = $parent->createElementNS($namespace, $qualifiedName);
47
        } else {
48
            $doc = $parent->ownerDocument;
49
            Assert::notNull($doc);
50
            $e = $doc->createElementNS($namespace, $qualifiedName);
51
        }
52
53
        $parent->appendChild($e);
54
55
        return $e;
56
    }
57
58
59
    /**
60
     * @template T of \SimpleSAML\XMLSchema\Type\Interface\ValueTypeInterface
61
     * @param \DOMElement     $xml The element where we should search for the attribute.
62
     * @param string          $name The name of the attribute.
63
     * @param class-string<T> $type The type of the attribute value.
64
     * @return T
65
     *
66
     * @throws \SimpleSAML\XMLSchema\Exception\MissingAttributeException if the attribute is missing from the element
67
     */
68
    public static function getAttribute(
69
        DOMElement $xml,
70
        string $name,
71
        string $type = StringValue::class,
72
    ): ValueTypeInterface {
73
        Assert::isAOf($type, ValueTypeInterface::class);
74
75
        $prefix = static::getNamespacePrefix();
76
        $localName = static::getLocalName();
77
        $qName = $prefix ? ($prefix . ':' . $localName) : $localName;
78
        Assert::true(
79
            $xml->hasAttribute($name),
80
            sprintf('Missing \'%s\' attribute on %s.', $name, $qName),
81
            MissingAttributeException::class,
82
        );
83
84
        $value = $xml->getAttribute($name);
85
        return ($type === QNameValue::class) ? QNameValue::fromDocument($value, $xml) : $type::fromString($value);
86
    }
87
88
89
    /**
90
     * Get the value of an attribute from a given element.
91
     *
92
     * @template T of \SimpleSAML\XMLSchema\Type\Interface\ValueTypeInterface
93
     * @param \DOMElement  $xml The element where we should search for the attribute.
94
     * @param string       $name The name of the attribute.
95
     * @param class-string<T> $type The type of the attribute value.
96
     * @param \SimpleSAML\XMLSchema\Type\Interface\ValueTypeInterface|null $default
97
     *   The default to return in case the attribute does not exist and it is optional.
98
     * @return ($default is \SimpleSAML\XMLSchema\Type\Interface\ValueTypeInterface ? T : T|null)
99
     */
100
    public static function getOptionalAttribute(
101
        DOMElement $xml,
102
        string $name,
103
        string $type = StringValue::class,
104
        ?ValueTypeInterface $default = null,
105
    ): ?ValueTypeInterface {
106
        Assert::nullOrIsInstanceOf($default, $type);
107
108
        if (!$xml->hasAttribute($name)) {
109
            return $default;
110
        }
111
112
        return self::getAttribute($xml, $name, $type);
113
    }
114
115
116
    /**
117
     * Static method that processes a fully namespaced class name and returns the name of the class from it.
118
     *
119
     * @param string $class
120
     * @return string
121
     */
122
    public static function getClassName(string $class): string
123
    {
124
        $ncName = join('', array_slice(explode('\\', $class), -1));
125
        Assert::validNCName($ncName, SchemaViolationException::class);
126
        return $ncName;
127
    }
128
129
130
    /**
131
     * Get the XML qualified name (prefix:name) of the element represented by this class.
132
     *
133
     * @return string
134
     */
135
    public function getQualifiedName(): string
136
    {
137
        $prefix = static::getNamespacePrefix();
138
        $qName = $prefix ? ($prefix . ':' . static::getLocalName()) : static::getLocalName();
139
        Assert::validQName($qName, SchemaViolationException::class);
140
        return $qName;
141
    }
142
143
144
    /**
145
     * Extract localized names from the children of a given element.
146
     *
147
     * @param \DOMElement $parent The element we want to search.
148
     * @return array<static> An array of objects of this class.
149
     */
150
    public static function getChildrenOfClass(DOMElement $parent): array
151
    {
152
        $ret = [];
153
        foreach ($parent->childNodes as $node) {
154
            if (
155
                $node instanceof DOMElement
156
                && $node->namespaceURI === static::getNamespaceURI()
157
                && $node->localName === static::getLocalName()
158
            ) {
159
                $ret[] = static::fromXML($node);
160
            }
161
        }
162
163
        return $ret;
164
    }
165
166
167
    /**
168
     * Get the namespace for the element.
169
     *
170
     * @return string|null
171
     */
172
    public static function getNamespaceURI(): ?string
173
    {
174
        Assert::true(
175
            defined('static::NS'),
176
            self::getClassName(static::class)
177
            . '::NS constant must be defined and set to the namespace for the XML-class it represents.',
178
            RuntimeException::class,
179
        );
180
        // @phpstan-ignore classConstant.notFound
181
        Assert::nullOrValidURI(static::NS, SchemaViolationException::class); // @phpstan-ignore-line
182
183
        // @phpstan-ignore classConstant.notFound
184
        return static::NS; // @phpstan-ignore-line
185
    }
186
187
188
    /**
189
     * Get the namespace-prefix for the element.
190
     *
191
     * @return string
192
     */
193
    public static function getNamespacePrefix(): string
194
    {
195
        Assert::true(
196
            defined('static::NS_PREFIX'),
197
            self::getClassName(static::class)
198
            . '::NS_PREFIX constant must be defined and set to the namespace prefix for the XML-class it represents.',
199
            RuntimeException::class,
200
        );
201
202
        // @phpstan-ignore classConstant.notFound
203
        return strval(static::NS_PREFIX); // @phpstan-ignore-line
204
    }
205
206
207
    /**
208
     * Get the local name for the element.
209
     *
210
     * @return string
211
     */
212
    public static function getLocalName(): string
213
    {
214
        if (defined('static::LOCALNAME')) {
215
            $ncName = static::LOCALNAME;
216
        } else {
217
            $ncName = self::getClassName(static::class);
218
        }
219
220
        Assert::validNCName($ncName, SchemaViolationException::class);
221
        return $ncName;
222
    }
223
224
225
    /**
226
     * Whether the element may be normalized.
227
     *
228
     * @return bool
229
     */
230
    public static function getNormalization(): bool
231
    {
232
        if (defined('static::NORMALIZATION')) {
233
            $normalization = static::NORMALIZATION;
0 ignored issues
show
Bug introduced by
The constant SimpleSAML\XML\AbstractElement::NORMALIZATION was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
234
        } else {
235
            $normalization = true;
236
        }
237
238
        Assert::boolean($normalization, RuntimeException::class);
239
        return $normalization;
240
    }
241
242
243
    /**
244
     * Test if an object, at the state it's in, would produce an empty XML-element
245
     *
246
     * @codeCoverageIgnore
247
     * @return bool
248
     */
249
    public function isEmptyElement(): bool
250
    {
251
        return false;
252
    }
253
}
254