1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
namespace Paysera\PhpStormHelper\Service; |
6
|
|
|
|
7
|
|
|
use DOMDocument; |
8
|
|
|
use DOMElement; |
9
|
|
|
use DOMNode; |
10
|
|
|
use RuntimeException; |
11
|
|
|
|
12
|
|
|
class DomHelper |
13
|
|
|
{ |
14
|
24 |
View Code Duplication |
public function loadDocument(string $pathToXml): DOMDocument |
|
|
|
|
15
|
|
|
{ |
16
|
24 |
|
if (!file_exists($pathToXml)) { |
17
|
|
|
throw new RuntimeException(sprintf('File %s does not exist', $pathToXml)); |
18
|
|
|
} |
19
|
|
|
|
20
|
24 |
|
$document = $this->createEmptyDomDocument(); |
21
|
24 |
|
$this->loadXmlContents($document, $pathToXml); |
22
|
24 |
|
return $document; |
23
|
|
|
} |
24
|
|
|
|
25
|
6 |
View Code Duplication |
public function loadOrCreateDocument(string $pathToXml): DOMDocument |
|
|
|
|
26
|
|
|
{ |
27
|
6 |
|
$document = $this->createEmptyDomDocument(); |
28
|
|
|
|
29
|
6 |
|
if (!file_exists($pathToXml)) { |
30
|
3 |
|
return $document; |
31
|
|
|
} |
32
|
|
|
|
33
|
3 |
|
$this->loadXmlContents($document, $pathToXml); |
34
|
|
|
|
35
|
3 |
|
return $document; |
36
|
|
|
} |
37
|
|
|
|
38
|
30 |
|
private function createEmptyDomDocument(): DOMDocument |
39
|
|
|
{ |
40
|
30 |
|
$document = new DOMDocument(); |
41
|
30 |
|
$document->preserveWhiteSpace = true; |
42
|
30 |
|
$document->formatOutput = true; |
43
|
30 |
|
return $document; |
44
|
|
|
} |
45
|
|
|
|
46
|
27 |
|
private function loadXmlContents(DOMDocument $document, string $pathToXml) |
47
|
|
|
{ |
48
|
27 |
|
libxml_clear_errors(); |
49
|
27 |
|
if (!$document->loadXML(file_get_contents($pathToXml))) { |
50
|
|
|
$error = libxml_get_last_error(); |
51
|
|
|
$message = $error === false ? 'Cannot load XML file' : $error->message; |
52
|
|
|
throw new RuntimeException($message); |
53
|
|
|
} |
54
|
27 |
|
} |
55
|
|
|
|
56
|
30 |
|
public function saveDocument(string $path, DOMDocument $document) |
57
|
|
|
{ |
58
|
30 |
|
file_put_contents($path, $document->saveXML()); |
59
|
30 |
|
} |
60
|
|
|
|
61
|
24 |
|
public function findNode(DOMNode $node, string $tagName, array $attributes = null): DOMNode |
62
|
|
|
{ |
63
|
24 |
|
$node = $this->findOptionalNode($node, $tagName, $attributes); |
64
|
24 |
|
if ($node === null) { |
65
|
|
|
throw new RuntimeException(sprintf('Node <%s> not found', $tagName)); |
66
|
|
|
} |
67
|
|
|
|
68
|
24 |
|
return $node; |
69
|
|
|
} |
70
|
|
|
|
71
|
30 |
|
public function findOptionalNode(DOMNode $node, string $tagName, array $attributes = null) |
72
|
|
|
{ |
73
|
30 |
|
$nodes = $this->findNodes($node, $tagName, $attributes); |
74
|
30 |
|
if (count($nodes) > 1) { |
75
|
|
|
throw new RuntimeException(sprintf('Expected only single <%s> tag', $tagName)); |
76
|
30 |
|
} elseif (count($nodes) === 0) { |
77
|
26 |
|
return null; |
78
|
|
|
} |
79
|
|
|
|
80
|
27 |
|
return $nodes[0]; |
81
|
|
|
} |
82
|
|
|
|
83
|
30 |
|
public function findOrCreateChildNode(DOMNode $node, string $tagName, array $attributes = null): DOMElement |
84
|
|
|
{ |
85
|
30 |
|
$internalNode = $this->findOptionalNode($node, $tagName, $attributes); |
86
|
30 |
|
if ($internalNode === null) { |
87
|
26 |
|
$ownerDocument = $node instanceof DOMDocument ? $node : $node->ownerDocument; |
88
|
26 |
|
$internalNode = $ownerDocument->createElement($tagName); |
89
|
26 |
|
$this->applyAttributesToElement($internalNode, $attributes ?? []); |
90
|
26 |
|
$node->appendChild($internalNode); |
91
|
|
|
} |
92
|
30 |
|
return $internalNode; |
93
|
|
|
} |
94
|
|
|
|
95
|
28 |
|
public function applyAttributesToElement(DOMElement $node, array $attributes) |
96
|
|
|
{ |
97
|
28 |
|
foreach ($attributes as $name => $value) { |
98
|
15 |
|
$node->setAttribute($name, $value); |
99
|
|
|
} |
100
|
28 |
|
} |
101
|
|
|
|
102
|
|
|
/** |
103
|
|
|
* @param DOMNode $node |
104
|
|
|
* @param string $tagName |
105
|
|
|
* @param array|null $attributes |
106
|
|
|
* @return array|DomNode[] |
107
|
|
|
*/ |
108
|
30 |
|
public function findNodes(DOMNode $node, string $tagName, array $attributes = null): array |
109
|
|
|
{ |
110
|
30 |
|
$result = []; |
111
|
|
|
|
112
|
|
|
/** @var DOMNode $childNode */ |
113
|
30 |
|
foreach ($node->childNodes as $childNode) { |
114
|
29 |
|
if ($childNode->nodeName === $tagName && $this->matchesAttributes($childNode, $attributes)) { |
115
|
29 |
|
$result[] = $childNode; |
116
|
|
|
} |
117
|
|
|
} |
118
|
|
|
|
119
|
30 |
|
return $result; |
120
|
|
|
} |
121
|
|
|
|
122
|
29 |
|
private function matchesAttributes(DOMNode $childNode, array $attributes = null): bool |
123
|
|
|
{ |
124
|
29 |
|
if ($attributes === null) { |
125
|
26 |
|
return true; |
126
|
|
|
} |
127
|
|
|
|
128
|
29 |
|
foreach ($attributes as $key => $value) { |
129
|
29 |
|
$attributeNode = $childNode->attributes->getNamedItem($key); |
130
|
29 |
|
$attributeValue = $attributeNode !== null ? $attributeNode->nodeValue : null; |
131
|
29 |
|
if ($attributeValue !== $value) { |
132
|
29 |
|
return false; |
133
|
|
|
} |
134
|
|
|
} |
135
|
|
|
|
136
|
18 |
|
return true; |
137
|
|
|
} |
138
|
|
|
|
139
|
11 |
|
public function removeNodes(DOMElement $parentNode, string $tagName, array $attributes = null) |
140
|
|
|
{ |
141
|
11 |
|
$mappings = $this->findNodes($parentNode, $tagName, $attributes); |
142
|
11 |
|
foreach ($mappings as $mapping) { |
143
|
3 |
|
$parentNode->removeChild($mapping); |
144
|
|
|
} |
145
|
11 |
|
} |
146
|
|
|
} |
147
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.