Issues (343)

src/XML/AbstractElement.php (4 issues)

Labels
Severity
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;
0 ignored issues
show
The type SimpleSAML\XMLSchema\Type\QNameValue was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
15
use SimpleSAML\XMLSchema\Type\StringValue;
0 ignored issues
show
The type SimpleSAML\XMLSchema\Type\StringValue was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
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
29
    SerializableElementInterface,
30
    ElementInterface
31
{
32
    use SerializableElementTrait;
33
34
35
    /**
36
     * Create a document structure for this element
37
     *
38
     * @param \DOMElement|null $parent The element we should append to.
39
     */
40
    public function instantiateParentElement(?DOMElement $parent = null): DOMElement
41
    {
42
        $qualifiedName = $this->getQualifiedName();
43
        $namespace = static::getNamespaceURI();
44
45
        if ($parent === null) {
46
            $parent = DOMDocumentFactory::create();
0 ignored issues
show
The type SimpleSAML\XML\DOMDocumentFactory was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
47
            $e = $parent->createElementNS($namespace, $qualifiedName);
48
        } else {
49
            $doc = $parent->ownerDocument;
50
            Assert::notNull($doc);
51
            $e = $doc->createElementNS($namespace, $qualifiedName);
52
        }
53
54
        $parent->appendChild($e);
55
56
        return $e;
57
    }
58
59
60
    /**
61
     * @template T of \SimpleSAML\XMLSchema\Type\Interface\ValueTypeInterface
62
     * @param \DOMElement     $xml The element where we should search for the attribute.
63
     * @param string          $name The name of the attribute.
64
     * @param class-string<T> $type The type of the attribute value.
65
     * @return T
66
     *
67
     * @throws \SimpleSAML\XMLSchema\Exception\MissingAttributeException if the attribute is missing from the element
68
     */
69
    public static function getAttribute(
70
        DOMElement $xml,
71
        string $name,
72
        string $type = StringValue::class,
73
    ): ValueTypeInterface {
74
        Assert::isAOf($type, ValueTypeInterface::class);
75
76
        $prefix = static::getNamespacePrefix();
77
        $localName = static::getLocalName();
78
        $qName = $prefix ? ($prefix . ':' . $localName) : $localName;
79
        Assert::true(
80
            $xml->hasAttribute($name),
81
            sprintf('Missing \'%s\' attribute on %s.', $name, $qName),
82
            MissingAttributeException::class,
83
        );
84
85
        $value = $xml->getAttribute($name);
86
        return ($type === QNameValue::class) ? QNameValue::fromDocument($value, $xml) : $type::fromString($value);
87
    }
88
89
90
    /**
91
     * Get the value of an attribute from a given element.
92
     *
93
     * @template T of \SimpleSAML\XMLSchema\Type\Interface\ValueTypeInterface
94
     * @param \DOMElement  $xml The element where we should search for the attribute.
95
     * @param string       $name The name of the attribute.
96
     * @param class-string<T> $type The type of the attribute value.
97
     * @param \SimpleSAML\XMLSchema\Type\Interface\ValueTypeInterface|null $default
98
     *   The default to return in case the attribute does not exist and it is optional.
99
     * @return ($default is \SimpleSAML\XMLSchema\Type\Interface\ValueTypeInterface ? T : T|null)
100
     */
101
    public static function getOptionalAttribute(
102
        DOMElement $xml,
103
        string $name,
104
        string $type = StringValue::class,
105
        ?ValueTypeInterface $default = null,
106
    ): ?ValueTypeInterface {
107
        Assert::nullOrIsInstanceOf($default, $type);
108
109
        if (!$xml->hasAttribute($name)) {
110
            return $default;
111
        }
112
113
        return self::getAttribute($xml, $name, $type);
114
    }
115
116
117
    /**
118
     * Static method that processes a fully namespaced class name and returns the name of the class from it.
119
     *
120
     * @param string $class
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
    public function getQualifiedName(): string
134
    {
135
        $prefix = static::getNamespacePrefix();
136
        $qName = $prefix ? ($prefix . ':' . static::getLocalName()) : static::getLocalName();
137
        Assert::validQName($qName, SchemaViolationException::class);
138
        return $qName;
139
    }
140
141
142
    /**
143
     * Extract localized names from the children of a given element.
144
     *
145
     * @param \DOMElement $parent The element we want to search.
146
     * @return array<static> An array of objects of this class.
147
     */
148
    public static function getChildrenOfClass(DOMElement $parent): array
149
    {
150
        $ret = [];
151
        foreach ($parent->childNodes as $node) {
152
            if (
153
                $node instanceof DOMElement
154
                && $node->namespaceURI === static::getNamespaceURI()
155
                && $node->localName === static::getLocalName()
156
            ) {
157
                $ret[] = static::fromXML($node);
158
            }
159
        }
160
161
        return $ret;
162
    }
163
164
165
    /**
166
     * Get the namespace for the element.
167
     */
168
    public static function getNamespaceURI(): ?string
169
    {
170
        Assert::true(
171
            defined('static::NS'),
172
            self::getClassName(static::class)
173
            . '::NS constant must be defined and set to the namespace for the XML-class it represents.',
174
            RuntimeException::class,
175
        );
176
        // @phpstan-ignore classConstant.notFound
177
        Assert::nullOrValidURI(static::NS, SchemaViolationException::class); // @phpstan-ignore-line
178
179
        // @phpstan-ignore classConstant.notFound
180
        return static::NS; // @phpstan-ignore-line
181
    }
182
183
184
    /**
185
     * Get the namespace-prefix for the element.
186
     */
187
    public static function getNamespacePrefix(): string
188
    {
189
        Assert::true(
190
            defined('static::NS_PREFIX'),
191
            self::getClassName(static::class)
192
            . '::NS_PREFIX constant must be defined and set to the namespace prefix for the XML-class it represents.',
193
            RuntimeException::class,
194
        );
195
196
        // @phpstan-ignore classConstant.notFound
197
        return strval(static::NS_PREFIX); // @phpstan-ignore-line
198
    }
199
200
201
    /**
202
     * Get the local name for the element.
203
     */
204
    public static function getLocalName(): string
205
    {
206
        if (defined('static::LOCALNAME')) {
207
            $ncName = static::LOCALNAME;
208
        } else {
209
            $ncName = self::getClassName(static::class);
210
        }
211
212
        Assert::validNCName($ncName, SchemaViolationException::class);
213
        return $ncName;
214
    }
215
216
217
    /**
218
     * Whether the element may be normalized.
219
     */
220
    public static function getNormalization(): bool
221
    {
222
        if (defined('static::NORMALIZATION')) {
223
            $normalization = static::NORMALIZATION;
0 ignored issues
show
The constant SimpleSAML\XML\AbstractElement::NORMALIZATION was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
224
        } else {
225
            $normalization = true;
226
        }
227
228
        Assert::boolean($normalization, RuntimeException::class);
229
        return $normalization;
230
    }
231
232
233
    /**
234
     * Test if an object, at the state it's in, would produce an empty XML-element
235
     *
236
     * @codeCoverageIgnore
237
     */
238
    public function isEmptyElement(): bool
239
    {
240
        return false;
241
    }
242
}
243