Passed
Pull Request — master (#15)
by Tim
01:47
created

AbstractElement::isEmptyElement()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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