1 | <?php |
||||
2 | |||||
3 | namespace Horat1us\Services; |
||||
4 | |||||
5 | use Horat1us\XmlConvertibleInterface; |
||||
6 | use Horat1us\XmlConvertibleObject; |
||||
7 | |||||
8 | /** |
||||
9 | * Class XmlParserService |
||||
10 | * @package Horat1us\Services |
||||
11 | */ |
||||
12 | class XmlParserService |
||||
13 | { |
||||
14 | /** |
||||
15 | * @var \DOMNode |
||||
16 | */ |
||||
17 | protected $element; |
||||
18 | |||||
19 | /** |
||||
20 | * @var XmlConvertibleInterface[] |
||||
21 | */ |
||||
22 | protected $aliases; |
||||
23 | |||||
24 | /** |
||||
25 | * XmlParserService constructor. |
||||
26 | * @param $document |
||||
27 | * @param array $aliases |
||||
28 | */ |
||||
29 | public function __construct($document, array $aliases = []) |
||||
30 | { |
||||
31 | 15 | $this |
|||
32 | ->setAliases($aliases) |
||||
33 | ->setDocument($document); |
||||
34 | 15 | } |
|||
35 | 12 | ||||
36 | 11 | public function __clone() |
|||
37 | { |
||||
38 | 8 | $this->element = clone $this->element; |
|||
39 | } |
||||
40 | 8 | ||||
41 | 8 | /** |
|||
42 | * @return XmlConvertibleInterface |
||||
43 | */ |
||||
44 | public function convert() |
||||
45 | { |
||||
46 | 10 | $nodeObject = new XmlConvertibleObject($this->element->nodeName); |
|||
47 | foreach ($this->aliases as $key => $alias) { |
||||
48 | if ($this->getIsAliasMatch($key, $alias)) { |
||||
49 | 10 | $nodeObject = clone $alias; |
|||
50 | } |
||||
51 | 10 | } |
|||
52 | 10 | ||||
53 | if ($this->element->hasChildNodes()) { |
||||
54 | 10 | $this->convertChildren($nodeObject); |
|||
55 | 8 | } |
|||
56 | if ($this->element->hasAttributes()) { |
||||
57 | 10 | $this->convertAttributes($nodeObject); |
|||
58 | 9 | } |
|||
59 | |||||
60 | return $nodeObject; |
||||
61 | 10 | } |
|||
62 | |||||
63 | /** |
||||
64 | * @param XmlConvertibleInterface $object |
||||
65 | * @return $this |
||||
66 | */ |
||||
67 | public function convertChildren(XmlConvertibleInterface &$object) |
||||
68 | 8 | { |
|||
69 | $children = []; |
||||
70 | 8 | $service = clone $this; |
|||
71 | 8 | ||||
72 | foreach ($this->element->childNodes as $childNode) { |
||||
73 | 8 | $service->setDocument($childNode); |
|||
74 | 8 | $children[] = $service->convert(); |
|||
75 | 8 | } |
|||
76 | $object->setXmlChildren($children); |
||||
77 | 8 | ||||
78 | return $this; |
||||
79 | 8 | } |
|||
80 | |||||
81 | public function convertAttributes(XmlConvertibleInterface &$object) |
||||
82 | 9 | { |
|||
83 | $properties = $object->getXmlProperties(); |
||||
84 | 9 | /** @var \DOMAttr $attribute */ |
|||
85 | foreach ($this->element->attributes as $attribute) { |
||||
86 | 9 | if ( |
|||
87 | !$object instanceof XmlConvertibleObject |
||||
88 | 9 | && !in_array($attribute->name, $properties) |
|||
89 | 9 | ) { |
|||
90 | throw new \UnexpectedValueException( |
||||
91 | 1 | get_class($object) . ' must have defined ' . $attribute->name . ' XML property', |
|||
92 | 1 | 4 |
|||
93 | 1 | ); |
|||
94 | } |
||||
95 | $object->{$attribute->name} = $attribute->value; |
||||
96 | 9 | } |
|||
97 | } |
||||
98 | 9 | ||||
99 | /** |
||||
100 | * @param \DOMNode|\DOMDocument $document |
||||
101 | * @return $this |
||||
102 | */ |
||||
103 | public function setDocument($document) |
||||
104 | 12 | { |
|||
105 | if ($document instanceof \DOMDocument) { |
||||
106 | 12 | return $this->setDocument($document->firstChild); |
|||
107 | 8 | } |
|||
108 | |||||
109 | if (!$document instanceof \DOMNode) { |
||||
0 ignored issues
–
show
introduced
by
![]() |
|||||
110 | 12 | throw new \InvalidArgumentException("Document must be instance of DOMElement or DOMNode"); |
|||
111 | 1 | } |
|||
112 | $this->element = $document; |
||||
113 | 11 | ||||
114 | return $this; |
||||
115 | 11 | } |
|||
116 | |||||
117 | /** |
||||
118 | * @return \DOMElement |
||||
119 | */ |
||||
120 | public function getDocument() |
||||
121 | 1 | { |
|||
122 | return $this->element; |
||||
123 | 1 | } |
|||
124 | |||||
125 | /** |
||||
126 | * @param XmlConvertibleInterface[]|string $aliases |
||||
127 | * @return $this |
||||
128 | */ |
||||
129 | public function setAliases(array $aliases = []) |
||||
130 | 15 | { |
|||
131 | $this->aliases = []; |
||||
132 | 15 | ||||
133 | foreach ($aliases as $key => $element) { |
||||
134 | 15 | $element = $this->mapAlias($element); |
|||
135 | $this->aliases[$this->mapKey($key, $element)] = $element; |
||||
136 | 13 | } |
|||
137 | 15 | ||||
138 | return $this; |
||||
139 | 10 | } |
|||
140 | 12 | ||||
141 | /** |
||||
142 | 12 | * @return XmlConvertibleInterface[] |
|||
143 | */ |
||||
144 | public function getAliases() |
||||
145 | { |
||||
146 | return $this->aliases; |
||||
147 | } |
||||
148 | 1 | ||||
149 | /** |
||||
150 | 1 | * @param string|XmlConvertibleInterface $element |
|||
151 | * @return XmlConvertibleInterface |
||||
152 | */ |
||||
153 | protected function mapAlias($element) |
||||
154 | { |
||||
155 | if ($element instanceof XmlConvertibleInterface) { |
||||
156 | return $element; |
||||
157 | 13 | } |
|||
158 | if (!is_string($element)) { |
||||
0 ignored issues
–
show
|
|||||
159 | 13 | throw new \UnexpectedValueException( |
|||
160 | 10 | "All aliases must be instance or class implements " . XmlConvertibleInterface::class |
|||
161 | ); |
||||
162 | 12 | } |
|||
163 | 3 | ||||
164 | 3 | // Additional type-checking |
|||
165 | return $this->mapAlias(new $element()); |
||||
166 | } |
||||
167 | |||||
168 | /** |
||||
169 | 10 | * @param string|integer $key |
|||
170 | * @param XmlConvertibleInterface $element |
||||
171 | * @return string |
||||
172 | */ |
||||
173 | protected function mapKey($key, XmlConvertibleInterface $element): string |
||||
174 | { |
||||
175 | return is_numeric($key) |
||||
176 | ? $element->getXmlElementName() |
||||
177 | 10 | : $key; |
|||
178 | } |
||||
179 | 10 | ||||
180 | 1 | /** |
|||
181 | 10 | * @param string $key |
|||
182 | * @param XmlConvertibleInterface $element |
||||
183 | * @return bool |
||||
184 | */ |
||||
185 | protected function getIsAliasMatch(string $key, XmlConvertibleInterface $element): bool |
||||
186 | { |
||||
187 | if ($this->element->nodeName !== $key) { |
||||
188 | return false; |
||||
189 | 10 | } |
|||
190 | |||||
191 | 10 | return array_reduce( |
|||
192 | 7 | array_filter( |
|||
193 | $element->getXmlProperties(), |
||||
194 | function ($property) use ($element) { |
||||
195 | 9 | return empty($element->{$property}); |
|||
196 | } |
||||
197 | 6 | ), |
|||
198 | 9 | function (bool $carry, $property) use ($element) { |
|||
199 | 9 | return $carry && $this->element->attributes->getNamedItem($property) != $element->{$property}; |
|||
0 ignored issues
–
show
The method
getNamedItem() 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
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. ![]() |
|||||
200 | 6 | }, |
|||
201 | 9 | true |
|||
202 | ); |
||||
203 | } |
||||
204 | } |
||||
205 |