Passed
Push — develop ( 10444e...4631c2 )
by Mikaël
01:29
created

getElementByNameAndAttributes()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 2
c 1
b 0
f 0
dl 0
loc 5
ccs 0
cts 3
cp 0
rs 10
cc 1
nc 1
nop 2
crap 2
1
<?php
2
3
namespace WsdlToPhp\DomHandler;
4
5
use DOMAttr;
6
use DOMDocument;
7
use DOMElement;
8
use DOMNameSpaceNode;
9
use DOMNode;
10
use DOMNodeList;
11
use DOMXPath;
12
use InvalidArgumentException;
13
14
abstract class AbstractDomDocumentHandler
15
{
16
    protected DOMDocument $domDocument;
17
18
    protected ?ElementHandler $rootElement;
19
20
    public function __construct(DOMDocument $domDocument)
21
    {
22
        $this->domDocument = $domDocument;
23
        $this->initRootElement();
24
    }
25
26
    /**
27
     * @param DOMAttr|DOMElement|DOMNode|DOMNameSpaceNode $node
28
     */
29
    public function getHandler($node, int $index = -1): AbstractNodeHandler
30
    {
31
        if ($node instanceof DOMElement) {
32
            return $this->getElementHandler($node, $this, $index);
33
        }
34
        if ($node instanceof DOMAttr) {
35
            return $this->getAttributeHandler($node, $this, $index);
36
        }
37
        if ($node instanceof DOMNameSpaceNode) {
38
            return new NameSpaceHandler($node, $this, $index);
39
        }
40
41
        return $this->getNodeHandler($node, $this, $index);
42
    }
43
44
    public function getNodeByName(string $name): ?NodeHandler
45
    {
46
        return $this->domDocument->getElementsByTagName($name)->length > 0 ? $this->getNodeHandler($this->domDocument->getElementsByTagName($name)->item(0), $this) : null;
47
    }
48
49
    public function getElementByName(string $name): ?ElementHandler
50
    {
51
        $node = $this->getNodeByName($name);
52
        if ($node instanceof AbstractNodeHandler && $node->getNode() instanceof DOMElement) {
53
            return $this->getElementHandler($node->getNode(), $this);
54
        }
55
56
        return null;
57
    }
58
59
    /**
60
     * @return AbstractAttributeHandler[]|AbstractElementHandler[]|AbstractNodeHandler[]
61
     */
62
    public function getNodesByName(string $name, ?string $checkInstance = null): array
63
    {
64
        $nodes = [];
65
        if ($this->domDocument->getElementsByTagName($name)->length > 0) {
66
            foreach ($this->domDocument->getElementsByTagName($name) as $node) {
67
                if (is_null($checkInstance) || $node instanceof $checkInstance) {
68
                    $nodes[] = $this->getHandler($node, count($nodes));
69
                }
70
            }
71
        }
72
73
        return $nodes;
74
    }
75
76
    /**
77
     * @return AbstractAttributeHandler[]|AbstractElementHandler[]|AbstractNodeHandler[]
78
     */
79
    public function getElementsByName(string $name): array
80
    {
81
        return $this->getNodesByName($name, DOMElement::class);
82
    }
83
84
    /**
85
     * @param string[] $attributes
86
     *
87
     * @return AbstractAttributeHandler[]|AbstractElementHandler[]|AbstractNodeHandler[]
88
     */
89
    public function getElementsByNameAndAttributes(string $name, array $attributes, ?DOMNode $node = null): array
90
    {
91
        $matchingElements = $this->getElementsByName($name);
92
        if ((!empty($attributes) || $node instanceof DOMNode) && !empty($matchingElements)) {
93
            $nodes = $this->searchTagsByXpath($name, $attributes, $node);
94
95
            if (false !== $nodes) {
96
                $matchingElements = $this->getElementsHandlers($nodes);
97
            }
98
        }
99
100
        return $matchingElements;
101
    }
102
103
    /**
104
     * @param DOMNodeList<DOMAttr|DOMElement|DOMNode> $nodeList
105
     *
106
     * @return AbstractElementHandler[]
107
     */
108
    public function getElementsHandlers(DOMNodeList $nodeList): array
109
    {
110
        $nodes = [];
111
        if (0 === $nodeList->count()) {
112
            return $nodes;
113
        }
114
115
        $index = 0;
116
        foreach ($nodeList as $node) {
117
            if (!$node instanceof DOMElement) {
118
                continue;
119
            }
120
121
            $nodes[] = $this->getElementHandler($node, $this, $index);
122
            ++$index;
123
        }
124
125
        return $nodes;
126
    }
127
128
    /**
129
     * @param string[] $attributes
130
     *
131
     * @return DOMNodeList<DOMAttr|DOMElement|DOMNode>|false
132
     */
133
    public function searchTagsByXpath(string $name, array $attributes, ?DOMNode $node = null)
134
    {
135
        $xpath = new DOMXPath($node ? $node->ownerDocument : $this->domDocument);
0 ignored issues
show
Bug introduced by
It seems like $node ? $node->ownerDocument : $this->domDocument can also be of type null; however, parameter $document of DOMXPath::__construct() does only seem to accept DOMDocument, 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

135
        $xpath = new DOMXPath(/** @scrutinizer ignore-type */ $node ? $node->ownerDocument : $this->domDocument);
Loading history...
136
        $xQuery = sprintf("%s//*[local-name()='%s']", $node instanceof DOMNode ? '.' : '', $name);
137
        foreach ($attributes as $attributeName => $attributeValue) {
138
            if (false !== strpos($attributeValue, '*')) {
139
                $xQuery .= sprintf("[contains(@%s, '%s')]", $attributeName, str_replace('*', '', $attributeValue));
140
            } else {
141
                $xQuery .= sprintf("[@%s='%s']", $attributeName, $attributeValue);
142
            }
143
        }
144
145
        return $xpath->query($xQuery, $node);
146
    }
147
148
    /**
149
     * @param string[] $attributes
150
     */
151
    public function getElementByNameAndAttributes(string $name, array $attributes): ?ElementHandler
152
    {
153
        $elements = $this->getElementsByNameAndAttributes($name, $attributes);
154
155
        return array_shift($elements);
156
    }
157
158
    /**
159
     * Find valid root node (not a comment, at least a DOMElement node).
160
     *
161
     * @throws InvalidArgumentException
162
     */
163
    protected function initRootElement(): void
164
    {
165
        if ($this->domDocument->hasChildNodes()) {
166
            foreach ($this->domDocument->childNodes as $node) {
167
                if ($node instanceof DOMElement) {
168
                    $this->rootElement = $this->getElementHandler($node, $this);
169
170
                    break;
171
                }
172
            }
173
        } else {
174
            throw new InvalidArgumentException('Document seems to be invalid', __LINE__);
175
        }
176
    }
177
178
    abstract protected function getNodeHandler(DOMNode $node, AbstractDomDocumentHandler $domDocument, int $index = -1): NodeHandler;
179
180
    abstract protected function getElementHandler(DOMElement $element, AbstractDomDocumentHandler $domDocument, int $index = -1): ElementHandler;
181
182
    abstract protected function getAttributeHandler(DOMAttr $attribute, AbstractDomDocumentHandler $domDocument, int $index = -1): AttributeHandler;
183
}
184