DomElementImpl::setAttribute()   B
last analyzed

Complexity

Conditions 7
Paths 6

Size

Total Lines 19
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 14
c 1
b 0
f 0
nc 6
nop 4
dl 0
loc 19
rs 8.8333
1
<?php
2
3
namespace Xml\Impl\Instance;
4
5
use Xml\Impl\ModelInstanceImpl;
6
use Xml\Instance\{
7
    DomDocumentInterface,
8
    DomElementInterface,
9
    ModelElementInstanceInterface
10
};
11
use Xml\Impl\Util\XmlQName;
12
use Xml\Impl\Util\DomUtil;
13
14
class DomElementImpl implements DomElementInterface
15
{
16
    private const MODEL_ELEMENT_KEY = "example.modelElementRef";
17
    private const XMLNS_ATTRIBUTE_NS_URI = "http://www.w3.org/2000/xmlns/";
18
    private const XMLNS_ATTRIBUTE = "xmlns";
19
20
    private $element;
21
22
    private $document;
23
24
    private $modelElementInstance;
0 ignored issues
show
introduced by
The private property $modelElementInstance is not used, and could be removed.
Loading history...
25
26
    public function __construct(DomElementExt $element)
27
    {
28
        $this->element = $element;
29
        $this->document = $element->ownerDocument;
30
    }
31
32
    public function getElement(): ?DomElementExt
33
    {
34
        return $this->element;
35
    }
36
37
    public function getNamespaceURI(): string
38
    {
39
        return $this->element->namespaceURI;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->element->namespaceURI could return the type null which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
40
    }
41
42
    public function getLocalName(): string
43
    {
44
        return $this->element->localName;
45
    }
46
47
    public function getPrefix(): string
48
    {
49
        return $this->element->prefix;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->element->prefix could return the type null which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
50
    }
51
52
    public function getDocument(): ?DomDocumentInterface
53
    {
54
        $ownerDocument = $this->element->ownerDocument;
55
        if ($ownerDocument !== null) {
56
            return new DomDocumentImpl($ownerDocument);
57
        } else {
58
            return null;
59
        }
60
    }
61
62
    public function getRootElement(): ?DomElementInterface
63
    {
64
        $document = $this->getDocument();
65
        if ($document !== null) {
66
            return $document->getRootElement();
67
        } else {
68
            return null;
69
        }
70
    }
71
72
    public function getParentElement(): ?DomElementInterface
73
    {
74
        $parentNode = $this->element->parentNode;
75
        if ($parentNode !== null && $parentNode instanceof DomElementExt) {
76
            return new DomElementImpl($parentNode);
77
        } else {
78
            return null;
79
        }
80
    }
81
82
    public function getChildElements(): array
83
    {
84
        $childNodes = $this->element->childNodes;
85
        return DomUtil::filterNodeListForElements($childNodes);
86
    }
87
88
    /**
89
     * @param mixed $uris
90
     * @param string $elementName
91
     */
92
    public function getChildElementsByNameNs($uris, string $elementName): array
93
    {
94
        $childNodes = $this->element->childNodes;
95
        $result = [];
96
        if (is_string($uris)) {
97
            $result = DomUtil::filterNodeListByName($childNodes, $uris, $elementName);
98
        } elseif (is_array($uris)) {
99
            foreach ($uris as $uri) {
100
                $result = array_merge($result, DomUtil::filterNodeListByName($childNodes, $uri, $elementName));
101
            }
102
        }
103
        return $result;
104
    }
105
106
107
    public function getChildElementsByType(
108
        ModelInstanceImpl $modelInstance,
109
        string $elementType
110
    ): array {
111
        $childNodes = $this->element->childNodes;
112
        return DomUtil::filterNodeListByType($childNodes, $modelInstance, $elementType);
113
    }
114
115
    public function replaceChild(
116
        DomElementInterface $newChildDomElement,
117
        DomElementInterface $existingChildDomElement
118
    ): void {
119
        $newElement = $newChildDomElement->getElement();
120
        $existingElement = $existingChildDomElement->getElement();
121
        if ($this->element->replaceChild($newElement, $existingElement) === false) {
0 ignored issues
show
Bug introduced by
It seems like $existingElement can also be of type null; however, parameter $child of DOMNode::replaceChild() does only seem to accept DOMNode, maybe add an additional type check? ( Ignorable by Annotation )

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

121
        if ($this->element->replaceChild($newElement, /** @scrutinizer ignore-type */ $existingElement) === false) {
Loading history...
Bug introduced by
It seems like $newElement can also be of type null; however, parameter $node of DOMNode::replaceChild() does only seem to accept DOMNode, maybe add an additional type check? ( Ignorable by Annotation )

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

121
        if ($this->element->replaceChild(/** @scrutinizer ignore-type */ $newElement, $existingElement) === false) {
Loading history...
122
            throw new \DOMException("Unable to replace child element with new element");
123
        }
124
    }
125
126
    public function removeChild(DomElementInterface $childDomElement): bool
127
    {
128
        $childElement = $childDomElement->getElement();
129
        try {
130
            $this->element->removeChild($childElement);
0 ignored issues
show
Bug introduced by
It seems like $childElement can also be of type null; however, parameter $child of DOMNode::removeChild() does only seem to accept DOMNode, maybe add an additional type check? ( Ignorable by Annotation )

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

130
            $this->element->removeChild(/** @scrutinizer ignore-type */ $childElement);
Loading history...
131
            return true;
132
        } catch (\Exception $e) {
133
            return false;
134
        }
135
    }
136
137
    public function appendChild(DomElementInterface $childDomElement): void
138
    {
139
        $childElement = $childDomElement->getElement();
140
        $this->element->appendChild($childElement);
0 ignored issues
show
Bug introduced by
It seems like $childElement can also be of type null; however, parameter $node of DOMNode::appendChild() does only seem to accept DOMNode, maybe add an additional type check? ( Ignorable by Annotation )

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

140
        $this->element->appendChild(/** @scrutinizer ignore-type */ $childElement);
Loading history...
141
    }
142
143
    public function insertChildElementAfter(
144
        DomElementInterface $elementToInsert,
145
        ?DomElementInterface $insertAfter
146
    ): void {
147
        $newElement = $elementToInsert->getElement();
148
        if ($insertAfter === null) {
149
            $insertBeforeNode = $this->element->firstChild;
150
        } else {
151
            $insertBeforeNode = $insertAfter->getElement()->nextSibling;
152
        }
153
154
        if ($insertBeforeNode !== null) {
155
            $this->element->insertBefore($newElement, $insertBeforeNode);
0 ignored issues
show
Bug introduced by
It seems like $newElement can also be of type null; however, parameter $node of DOMNode::insertBefore() does only seem to accept DOMNode, maybe add an additional type check? ( Ignorable by Annotation )

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

155
            $this->element->insertBefore(/** @scrutinizer ignore-type */ $newElement, $insertBeforeNode);
Loading history...
156
        } else {
157
            $this->element->appendChild($newElement);
0 ignored issues
show
Bug introduced by
It seems like $newElement can also be of type null; however, parameter $node of DOMNode::appendChild() does only seem to accept DOMNode, maybe add an additional type check? ( Ignorable by Annotation )

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

157
            $this->element->appendChild(/** @scrutinizer ignore-type */ $newElement);
Loading history...
158
        }
159
    }
160
161
    public function hasAttribute(?string $namespaceUri, string $localName): bool
162
    {
163
        if ($namespaceUri === null) {
164
            return $this->element->hasAttribute($localName);
165
        } else {
166
            return $this->element->hasAttributeNS($namespaceUri, $localName);
167
        }
168
    }
169
170
    public function getAttribute(?string $namespaceUri, string $localName): ?string
171
    {
172
        $xmlQName = new XmlQName($this->getDocument(), $this, $namespaceUri, $localName);
173
        if ($xmlQName->hasLocalNamespace()) {
174
            $value = $this->element->getAttribute($xmlQName->getLocalName());
175
        } else {
176
            $value = $this->element->getAttributeNS($xmlQName->getNamespaceUri(), $xmlQName->getLocalName());
177
        }
178
179
        if (empty($value)) {
180
            return null;
181
        } else {
182
            return $value;
183
        }
184
    }
185
186
    public function setAttribute(?string $namespaceUri, string $localName, string $value, ?bool $isIdAttribute = false): void
187
    {
188
        $isIdAttribute = $isIdAttribute ?? false;
189
        $xmlQName = new XmlQName($this->getDocument(), $this, $namespaceUri, $localName);
190
        if ($xmlQName->hasLocalNamespace()) {
191
            $this->element->setAttribute($xmlQName->getLocalName(), $value);
192
            if ($isIdAttribute && !$this->element->getAttributeNode($localName)->isId()) {
193
                $this->element->setIdAttribute($xmlQName->getLocalName(), true);
194
            }
195
        } else {
196
            //PHP does not create prefix automatically, like Java
197
            $prefixedName = $xmlQName->getPrefixedName();
198
            if (strpos($prefixedName, ":") === false) {
199
                $prefix = $this->getDocument()->getUnusedGenericNsPrefix();
200
                $prefixedName = $prefix . ":" . $localName;
201
            }
202
            $this->element->setAttributeNS($xmlQName->getNamespaceUri(), $prefixedName, $value);
203
            if ($isIdAttribute && !$this->element->getAttributeNodeNS($xmlQName->getNamespaceUri(), $localName)->isId()) {
204
                $this->element->setIdAttributeNS($xmlQName->getNamespaceUri(), $xmlQName->getLocalName(), true);
205
            }
206
        }
207
    }
208
209
    public function setIdAttribute(?string $namespaceUri, string $localName, string $value): void
210
    {
211
        $namespaceUri = $namespaceUri ?? $this->getNamespaceURI();
212
        $this->setAttribute($namespaceUri, $localName, $value, true);
213
    }
214
215
    public function removeAttribute(?string $namespaceUri, string $localName): void
216
    {
217
        $namespaceUri = $namespaceUri ?? $this->getNamespaceURI();
218
        $xmlQName = new XmlQName($this->getDocument(), $this, $namespaceUri, $localName);
219
        if ($xmlQName->hasLocalNamespace()) {
220
            $this->element->removeAttribute($xmlQName->getLocalName());
221
        } else {
222
            $this->element->removeAttributeNS($xmlQName->getNamespaceUri(), $xmlQName->getLocalName());
223
        }
224
    }
225
226
    public function getTextContent(): string
227
    {
228
        return $this->element->textContent;
229
    }
230
231
    public function setTextContent(string $textContent): void
232
    {
233
        $this->element->nodeValue = $textContent;
234
    }
235
236
    public function addCDataSection(string $data): void
237
    {
238
        $document = $this->getDocument();
239
        $cdataSection = $document->createCDATASection($data);
0 ignored issues
show
Bug introduced by
The method createCDATASection() does not exist on Xml\Impl\Instance\DomDocumentImpl. ( Ignorable by Annotation )

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

239
        /** @scrutinizer ignore-call */ 
240
        $cdataSection = $document->createCDATASection($data);

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...
240
        $this->element->appendChild($cdataSection);
241
    }
242
243
    public function getModelElementInstance(): ?ModelElementInstanceInterface
244
    {
245
        //return $this->modelElementInstance;
246
        return $this->element->getUserData(self::MODEL_ELEMENT_KEY);
247
    }
248
249
    public function setModelElementInstance(ModelElementInstanceInterface $modelElementInstance): void
250
    {
251
        //$this->modelElementInstance = $modelElementInstance;
252
        $this->element->setUserData(self::MODEL_ELEMENT_KEY, $modelElementInstance, null);
253
    }
254
255
    /**
256
     * @return mixed
257
     */
258
    public function registerNamespace(?string $prefix, string $namespaceUri)
259
    {
260
        if ($prefix !== null) {
261
            $this->element->setAttributeNS(
262
                self::XMLNS_ATTRIBUTE_NS_URI,
263
                self::XMLNS_ATTRIBUTE . ":" . $prefix,
264
                $namespaceUri
265
            );
266
        } else {
267
            $lookupPrefix = $this->lookupPrefix($namespaceUri);
268
            if ($lookupPrefix === null) {
269
                if (array_key_exists($namespaceUri, XmlQName::KNOWN_PREFIXES)) {
270
                    $prefix = XmlQName::KNOWN_PREFIXES[$namespaceUri];
271
                }
272
                if (
273
                    $prefix !== null &&
274
                    $this->getRootElement() !== null &&
275
                    $this->getRootElement()->element->hasAttributeNS(self::XMLNS_ATTRIBUTE_NS_URI, $prefix)
276
                ) {
277
                    $prefix = null;
278
                }
279
                if ($prefix === null) {
280
                    $prefix = $this->getDocument()->getUnusedGenericNsPrefix();
281
                }
282
                $this->registerNamespace($prefix, $namespaceUri);
283
                return $prefix;
284
            } else {
285
                return $lookupPrefix;
286
            }
287
        }
288
    }
289
290
    public function lookupPrefix(string $namespaceUri): ?string
291
    {
292
        return $this->element->lookupPrefix($namespaceUri);
293
    }
294
295
    public function equals(?DomElementInterface $obj): bool
296
    {
297
        if ($obj === null) {
298
            return false;
299
        }
300
301
        return $this->element->isSameNode($obj->getElement());
0 ignored issues
show
Bug introduced by
It seems like $obj->getElement() can also be of type null; however, parameter $otherNode of DOMNode::isSameNode() does only seem to accept DOMNode, maybe add an additional type check? ( Ignorable by Annotation )

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

301
        return $this->element->isSameNode(/** @scrutinizer ignore-type */ $obj->getElement());
Loading history...
302
    }
303
}
304