Issues (21)

src/Chunk.php (4 issues)

1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\XML;
6
7
use DOMElement;
8
use SimpleSAML\XML\Assert\Assert;
9
use SimpleSAML\XML\DOMDocumentFactory;
10
use SimpleSAML\XML\Exception\{MissingAttributeException, SchemaViolationException};
11
use SimpleSAML\XML\SerializableElementTrait;
12
use SimpleSAML\XML\Type\{StringValue, ValueTypeInterface};
13
14
/**
15
 * Serializable class used to hold an XML element.
16
 *
17
 * @package simplesamlphp/xml-common
18
 */
19
final class Chunk implements SerializableElementInterface
20
{
21
    use SerializableElementTrait;
22
23
24
    /**
25
     * The localName of the element.
26
     *
27
     * @var string
28
     */
29
    protected string $localName;
30
31
    /**
32
     * The namespaceURI of this element.
33
     *
34
     * @var string|null
35
     */
36
    protected ?string $namespaceURI;
37
38
    /**
39
     * The prefix of this element.
40
     *
41
     * @var string
42
     */
43
    protected string $prefix;
44
45
46
    /**
47
     * Create an XML Chunk from a copy of the given \DOMElement.
48
     *
49
     * @param \DOMElement $xml The element we should copy.
50
     */
51
    public function __construct(
52
        protected DOMElement $xml,
53
    ) {
54
        $this->setLocalName($xml->localName);
55
        $this->setNamespaceURI($xml->namespaceURI);
56
        $this->setPrefix($xml->prefix);
57
    }
58
59
60
    /**
61
     * Collect the value of the localName-property
62
     *
63
     * @return string
64
     */
65
    public function getLocalName(): string
66
    {
67
        return $this->localName;
68
    }
69
70
71
    /**
72
     * Set the value of the localName-property
73
     *
74
     * @param string $localName
75
     * @throws \SimpleSAML\Assert\AssertionFailedException if $localName is an empty string
76
     */
77
    public function setLocalName(string $localName): void
78
    {
79
        Assert::validNCName($localName, SchemaViolationException::class); // Covers the empty string
80
        $this->localName = $localName;
81
    }
82
83
84
    /**
85
     * Collect the value of the namespaceURI-property
86
     *
87
     * @return string|null
88
     */
89
    public function getNamespaceURI(): ?string
90
    {
91
        return $this->namespaceURI;
92
    }
93
94
95
    /**
96
     * Set the value of the namespaceURI-property
97
     *
98
     * @param string|null $namespaceURI
99
     */
100
    protected function setNamespaceURI(?string $namespaceURI = null): void
101
    {
102
        Assert::nullOrValidURI($namespaceURI, SchemaViolationException::class);
103
        $this->namespaceURI = $namespaceURI;
104
    }
105
106
107
    /**
108
     * Get this \DOMElement.
109
     *
110
     * @return \DOMElement This element.
111
     */
112
    public function getXML(): DOMElement
113
    {
114
        return $this->xml;
115
    }
116
117
118
    /**
119
     * Collect the value of the prefix-property
120
     *
121
     * @return string
122
     */
123
    public function getPrefix(): string
124
    {
125
        return $this->prefix;
126
    }
127
128
129
    /**
130
     * Set the value of the prefix-property
131
     *
132
     * @param string|null $prefix
133
     */
134
    protected function setPrefix(?string $prefix = null): void
135
    {
136
        $this->prefix = strval($prefix);
137
    }
138
139
140
    /**
141
     * Get the XML qualified name (prefix:name, or just name when not prefixed)
142
     *  of the element represented by this class.
143
     *
144
     * @return string
145
     */
146
    public function getQualifiedName(): string
147
    {
148
        $prefix = $this->getPrefix();
149
150
        if (empty($prefix)) {
151
            return $this->getLocalName();
152
        } else {
153
            return $prefix . ':' . $this->getLocalName();
154
        }
155
    }
156
157
158
    /**
159
     * Get the value of an attribute from a given element.
160
     *
161
     * @template T of \SimpleSAML\XML\Type\ValueTypeInterface
162
     * @param \DOMElement     $xml The element where we should search for the attribute.
163
     * @param string          $name The name of the attribute.
164
     * @param class-string<T> $type The type of the attribute value.
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<T> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<T>.
Loading history...
165
     * @return T
166
     *
167
     * @throws \SimpleSAML\XML\Exception\MissingAttributeException if the attribute is missing from the element
168
     */
169
    public static function getAttribute(
170
        DOMElement $xml,
171
        string $name,
172
        string $type = StringValue::class,
173
    ): ValueTypeInterface {
174
        Assert::isAOf($type, ValueTypeInterface::class);
175
176
        Assert::true(
177
            $xml->hasAttribute($name),
178
            'Missing \'' . $name . '\' attribute on ' . $xml->prefix . ':' . $xml->localName . '.',
179
            MissingAttributeException::class,
180
        );
181
182
        $value = $xml->getAttribute($name);
183
        return $type::fromString($value);
184
    }
185
186
187
    /**
188
     * Get the value of an attribute from a given element.
189
     *
190
     * @template T of \SimpleSAML\XML\Type\ValueTypeInterface
191
     * @param \DOMElement  $xml The element where we should search for the attribute.
192
     * @param string       $name The name of the attribute.
193
     * @param class-string<T> $type The type of the attribute value.
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<T> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<T>.
Loading history...
194
     * @param \SimpleSAML\XML\Type\ValueTypeInterface|null $default
195
     *   The default to return in case the attribute does not exist and it is optional.
196
     * @return ($default is \SimpleSAML\XML\Type\ValueTypeInterface ? T : T|null)
197
     */
198
    public static function getOptionalAttribute(
199
        DOMElement $xml,
200
        string $name,
201
        string $type = StringValue::class,
202
        ?ValueTypeInterface $default = null,
203
    ): ?ValueTypeInterface {
204
        if (!$xml->hasAttribute($name)) {
205
            return $default;
206
        }
207
208
        return self::getAttribute($xml, $name, $type);
209
    }
210
211
212
    /**
213
     * Test if an object, at the state it's in, would produce an empty XML-element
214
     *
215
     * @return bool
216
     */
217
    public function isEmptyElement(): bool
218
    {
219
        /** @var \DOMElement $xml */
220
        $xml = $this->getXML();
221
        return ($xml->childNodes->length === 0) && ($xml->attributes->count() === 0);
0 ignored issues
show
The method count() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

221
        return ($xml->childNodes->length === 0) && ($xml->attributes->/** @scrutinizer ignore-call */ count() === 0);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
222
    }
223
224
225
    /**
226
     * @param \DOMElement $xml
227
     * @return static
228
     */
229
    public static function fromXML(DOMElement $xml): static
230
    {
231
        return new static($xml);
232
    }
233
234
235
    /**
236
     * Append this XML element to a different XML element.
237
     *
238
     * @param  \DOMElement|null $parent The element we should append this element to.
239
     * @return \DOMElement The new element.
240
     */
241
    public function toXML(?DOMElement $parent = null): DOMElement
242
    {
243
        if ($parent === null) {
244
            $doc = DOMDocumentFactory::create();
245
        } else {
246
            $doc = $parent->ownerDocument;
247
            Assert::notNull($doc);
248
        }
249
250
        if ($parent === null) {
251
            $parent = $doc;
252
        }
253
254
        $parent->appendChild($doc->importNode($this->getXML(), true));
0 ignored issues
show
The method appendChild() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

254
        $parent->/** @scrutinizer ignore-call */ 
255
                 appendChild($doc->importNode($this->getXML(), true));

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
255
256
        return $doc->documentElement;
257
    }
258
}
259