1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* This file is part of riesenia/pohoda package. |
5
|
|
|
* |
6
|
|
|
* Licensed under the MIT License |
7
|
|
|
* (c) RIESENIA.com |
8
|
|
|
*/ |
9
|
|
|
|
10
|
|
|
declare(strict_types=1); |
11
|
|
|
|
12
|
|
|
namespace Riesenia\Pohoda; |
13
|
|
|
|
14
|
|
|
use Riesenia\Pohoda; |
15
|
|
|
use Riesenia\Pohoda\ValueTransformer\ValueTransformerInterface; |
16
|
|
|
use SimpleXMLElement; |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* Base class for Pohoda objects. |
20
|
|
|
* |
21
|
|
|
* @method \void setNamespace(string $namespace) |
22
|
|
|
* @method \void setNodeName(string $nodeName) |
23
|
|
|
*/ |
24
|
|
|
abstract class AbstractAgenda |
25
|
|
|
{ |
26
|
|
|
use Pohoda\Common\OneDirectionalVariablesTrait; |
27
|
|
|
use Pohoda\Common\DirectionAsResponseTrait; |
28
|
|
|
|
29
|
|
|
/** @var array<string, mixed> */ |
30
|
|
|
protected array $data = []; |
31
|
|
|
|
32
|
|
|
/** @var string[] */ |
33
|
|
|
protected array $refElements = []; |
34
|
|
|
|
35
|
|
|
/** @var string[] */ |
36
|
|
|
protected array $directionalRefElements = []; |
37
|
|
|
|
38
|
|
|
/** @var array<string, Common\ElementAttributes> */ |
39
|
|
|
protected array $elementsAttributesMapper = []; |
40
|
|
|
|
41
|
|
|
/** @var Common\OptionsResolver[] */ |
42
|
|
|
private static array $resolvers = []; |
43
|
|
|
|
44
|
|
|
/** |
45
|
|
|
* Construct agenda using provided data. |
46
|
|
|
* |
47
|
|
|
* @param Common\NamespacesPaths $namespacesPaths |
48
|
|
|
* @param ValueTransformer\SanitizeEncoding $sanitizeEncoding |
49
|
|
|
* @param string $companyRegistrationNumber |
50
|
|
|
* @param bool $resolveOptions |
51
|
|
|
* @param Common\OptionsResolver\Normalizers\NormalizerFactory $normalizerFactory |
52
|
|
|
*/ |
53
|
179 |
|
public function __construct( |
54
|
|
|
protected readonly Common\NamespacesPaths $namespacesPaths, |
55
|
|
|
protected Pohoda\ValueTransformer\SanitizeEncoding $sanitizeEncoding, |
56
|
|
|
protected readonly string $companyRegistrationNumber, |
57
|
|
|
protected bool $resolveOptions = true, |
58
|
|
|
protected Common\OptionsResolver\Normalizers\NormalizerFactory $normalizerFactory = new Common\OptionsResolver\Normalizers\NormalizerFactory(), |
59
|
179 |
|
) {} |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* Import root |
63
|
|
|
* |
64
|
|
|
* string for xml node, null for not existing one |
65
|
|
|
* |
66
|
|
|
* @return string|null |
67
|
|
|
*/ |
68
|
3 |
|
public function getImportRoot(): ?string |
69
|
|
|
{ |
70
|
3 |
|
return null; |
71
|
|
|
} |
72
|
|
|
|
73
|
|
|
/** |
74
|
|
|
* Can read data recursively |
75
|
|
|
* |
76
|
|
|
* @return bool |
77
|
|
|
*/ |
78
|
1 |
|
public function canImportRecursive(): bool |
79
|
|
|
{ |
80
|
1 |
|
return false; |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* Set & resolve data options |
85
|
|
|
* Necessary for late setting when there is more options available |
86
|
|
|
* |
87
|
|
|
* @param array<string, mixed> $data |
88
|
|
|
* |
89
|
|
|
* @return $this |
90
|
|
|
*/ |
91
|
157 |
|
public function setData(array $data): self |
92
|
|
|
{ |
93
|
|
|
// resolve options |
94
|
157 |
|
$this->data = $this->resolveOptions ? $this->resolveOptions($data) : $data; |
95
|
|
|
|
96
|
157 |
|
return $this; |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
/** |
100
|
|
|
* Get XML. |
101
|
|
|
* |
102
|
|
|
* @return SimpleXMLElement |
103
|
|
|
*/ |
104
|
|
|
abstract public function getXML(): SimpleXMLElement; |
105
|
|
|
|
106
|
|
|
/** |
107
|
|
|
* Configure options for options resolver. |
108
|
|
|
* |
109
|
|
|
* @param Common\OptionsResolver $resolver |
110
|
|
|
* |
111
|
|
|
* @return void |
112
|
|
|
*/ |
113
|
|
|
abstract protected function configureOptions(Common\OptionsResolver $resolver): void; |
114
|
|
|
|
115
|
|
|
/** |
116
|
|
|
* Create XML. |
117
|
|
|
* |
118
|
|
|
* @return SimpleXMLElement |
119
|
|
|
*/ |
120
|
129 |
|
protected function createXML(): SimpleXMLElement |
121
|
|
|
{ |
122
|
129 |
|
$np = $this->namespacesPaths->allNamespaces(); |
123
|
129 |
|
return new SimpleXMLElement('<?xml version="1.0" encoding="' . $this->sanitizeEncoding->getEncoding() . '"?><root ' . \implode(' ', \array_map(function ($k, $v) { |
124
|
129 |
|
return 'xmlns:' . $k . '="' . $v . '"'; |
125
|
129 |
|
}, \array_keys($np), \array_values($np))) . '></root>'); |
126
|
|
|
} |
127
|
|
|
|
128
|
|
|
/** |
129
|
|
|
* Get namespace. |
130
|
|
|
* |
131
|
|
|
* @param string $short |
132
|
|
|
* |
133
|
|
|
* @return string |
134
|
|
|
*/ |
135
|
129 |
|
protected function namespace(string $short): string |
136
|
|
|
{ |
137
|
129 |
|
return $this->namespacesPaths->namespace($short); |
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
/** |
141
|
|
|
* Add batch elements. |
142
|
|
|
* |
143
|
|
|
* @param SimpleXMLElement $xml |
144
|
|
|
* @param string[] $elements |
145
|
|
|
* @param string|null $namespace |
146
|
|
|
* |
147
|
|
|
* @return void |
148
|
|
|
*/ |
149
|
115 |
|
protected function addElements(SimpleXMLElement $xml, array $elements, ?string $namespace = null): void |
150
|
|
|
{ |
151
|
115 |
|
foreach ($elements as $element) { |
152
|
115 |
|
if (!isset($this->data[$element])) { |
153
|
115 |
|
continue; |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
// ref element |
157
|
96 |
|
if (\in_array($element, $this->refElements)) { |
158
|
57 |
|
$this->addRefElement($xml, ($namespace ? $namespace . ':' : '') . $element, $this->data[$element], $namespace); |
159
|
57 |
|
continue; |
160
|
|
|
} |
161
|
94 |
|
if ($this->useOneDirectionalVariables && \in_array($element, $this->directionalRefElements)) { |
162
|
|
|
$this->addRefElement($xml, ($namespace ? $namespace . ':' : '') . $element, $this->data[$element], $namespace); |
163
|
|
|
continue; |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
// element attribute |
167
|
94 |
|
if (isset($this->elementsAttributesMapper[$element])) { |
168
|
11 |
|
$attrs = $this->elementsAttributesMapper[$element]; |
169
|
|
|
|
170
|
|
|
// get element |
171
|
11 |
|
$attrElement = $namespace ? $xml->children($namespace, true)->{$attrs->attrElement} : $xml->{$attrs->attrElement}; |
172
|
|
|
|
173
|
11 |
|
$sanitized = $this->sanitize($this->data[$element]); |
174
|
11 |
|
$attrs->attrNamespace ? $attrElement->addAttribute( |
175
|
11 |
|
$attrs->attrNamespace . ':' . $attrs->attrName, |
176
|
11 |
|
$sanitized, |
177
|
11 |
|
$this->namespace($attrs->attrNamespace), |
178
|
11 |
|
) |
179
|
11 |
|
: $attrElement->addAttribute($attrs->attrName, $sanitized); |
180
|
|
|
|
181
|
11 |
|
continue; |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
// Agenda object |
185
|
94 |
|
if ($this->data[$element] instanceof self) { |
186
|
|
|
// set namespace |
187
|
92 |
|
if ($namespace && \method_exists($this->data[$element], 'setNamespace')) { |
188
|
59 |
|
$this->data[$element]->setNamespace($namespace); |
189
|
|
|
} |
190
|
|
|
|
191
|
|
|
// set node name |
192
|
92 |
|
if (\method_exists($this->data[$element], 'setNodeName')) { |
193
|
59 |
|
$this->data[$element]->setNodeName($element); |
194
|
|
|
} |
195
|
|
|
|
196
|
92 |
|
$this->appendNode($xml, $this->data[$element]->getXML()); |
197
|
|
|
|
198
|
92 |
|
continue; |
199
|
|
|
} |
200
|
|
|
|
201
|
|
|
// array of Agenda objects |
202
|
89 |
|
if (\is_array($this->data[$element])) { |
203
|
35 |
|
$child = $namespace ? $xml->addChild($namespace . ':' . $element, '', $this->namespace($namespace)) : $xml->addChild($element); |
204
|
|
|
|
205
|
35 |
|
foreach ($this->data[$element] as $node) { |
206
|
35 |
|
$this->appendNode($child, $node->getXML()); |
207
|
|
|
} |
208
|
|
|
|
209
|
35 |
|
continue; |
210
|
|
|
} |
211
|
|
|
|
212
|
89 |
|
$sanitized = $this->sanitize($this->data[$element]); |
213
|
89 |
|
$namespace ? $xml->addChild($namespace . ':' . $element, $sanitized, $this->namespace($namespace)) : $xml->addChild($element, $sanitized); |
214
|
|
|
} |
215
|
|
|
} |
216
|
|
|
|
217
|
|
|
/** |
218
|
|
|
* Add ref element. |
219
|
|
|
* |
220
|
|
|
* @param SimpleXMLElement $xml |
221
|
|
|
* @param string $name |
222
|
|
|
* @param mixed $value |
223
|
|
|
* @param string|null $namespace |
224
|
|
|
* |
225
|
|
|
* @return SimpleXMLElement |
226
|
|
|
*/ |
227
|
65 |
|
protected function addRefElement(SimpleXMLElement $xml, string $name, mixed $value, ?string $namespace = null): SimpleXMLElement |
228
|
|
|
{ |
229
|
65 |
|
$node = $namespace ? $xml->addChild($name, '', $this->namespace($namespace)) : $xml->addChild($name); |
230
|
|
|
|
231
|
65 |
|
if (!\is_array($value)) { |
232
|
39 |
|
$value = ['ids' => $value]; |
233
|
|
|
} |
234
|
|
|
|
235
|
65 |
|
foreach ($value as $key => $value1) { |
236
|
65 |
|
if (\is_array($value1)) { |
237
|
2 |
|
if (array_is_list($value1)) { |
238
|
1 |
|
foreach ($value1 as $value2) { |
239
|
1 |
|
$node->addChild($namespace . ':' . $key, $this->sanitize($value2), $this->namespace(strval($namespace))); |
240
|
|
|
} |
241
|
|
|
} else { |
242
|
1 |
|
$node = $node->addChild($namespace . ':' . $key, '', $this->namespace(strval($namespace))); |
243
|
|
|
|
244
|
2 |
|
foreach ($value1 as $key2 => $value2) { |
245
|
1 |
|
$node->addChild('typ:' . $key2, $this->sanitize($value2), $this->namespace('typ')); |
246
|
|
|
} |
247
|
|
|
} |
248
|
|
|
} else { |
249
|
63 |
|
$node->addChild('typ:' . $key, $this->sanitize($value1), $this->namespace('typ')); |
250
|
|
|
} |
251
|
|
|
} |
252
|
|
|
|
253
|
65 |
|
return $node; |
254
|
|
|
} |
255
|
|
|
|
256
|
|
|
/** |
257
|
|
|
* Sanitize value to XML. |
258
|
|
|
* |
259
|
|
|
* @param mixed $value |
260
|
|
|
* |
261
|
|
|
* @return string |
262
|
|
|
*/ |
263
|
93 |
|
protected function sanitize(mixed $value): string |
264
|
|
|
{ |
265
|
93 |
|
$this->sanitizeEncoding->listingWithEncoding(); |
266
|
|
|
|
267
|
93 |
|
return \htmlspecialchars( |
268
|
93 |
|
\array_reduce( |
269
|
93 |
|
$this->sanitizeEncoding->getListing()->getTransformers(), |
270
|
93 |
|
function (string $value, ValueTransformerInterface $transformer): string { |
271
|
5 |
|
return $transformer->transform($value); |
272
|
93 |
|
}, |
273
|
93 |
|
strval($value), |
274
|
93 |
|
), |
275
|
93 |
|
); |
276
|
|
|
} |
277
|
|
|
|
278
|
|
|
/** |
279
|
|
|
* Append SimpleXMLElement to another SimpleXMLElement. |
280
|
|
|
* |
281
|
|
|
* @param SimpleXMLElement $xml |
282
|
|
|
* @param SimpleXMLElement $node |
283
|
|
|
* |
284
|
|
|
* @return void |
285
|
|
|
*/ |
286
|
94 |
|
protected function appendNode(SimpleXMLElement $xml, SimpleXMLElement $node): void |
287
|
|
|
{ |
288
|
94 |
|
$dom = \dom_import_simplexml($xml); |
289
|
94 |
|
$dom2 = \dom_import_simplexml($node); |
290
|
|
|
|
291
|
94 |
|
if (!$dom->ownerDocument) { |
292
|
|
|
// @codeCoverageIgnoreStart |
293
|
|
|
throw new \InvalidArgumentException('Invalid XML.'); |
294
|
|
|
} |
295
|
|
|
// @codeCoverageIgnoreEnd |
296
|
|
|
|
297
|
94 |
|
$dom->appendChild($dom->ownerDocument->importNode($dom2, true)); |
298
|
|
|
} |
299
|
|
|
|
300
|
|
|
/** |
301
|
|
|
* Resolve options. |
302
|
|
|
* |
303
|
|
|
* @param array<string,mixed> $data |
304
|
|
|
* |
305
|
|
|
* @return array<string,mixed> |
306
|
|
|
*/ |
307
|
152 |
|
protected function resolveOptions(array $data): array |
308
|
|
|
{ |
309
|
152 |
|
$class = \get_class($this); |
310
|
152 |
|
$opt = '_' . intval($this->useOneDirectionalVariables); |
311
|
|
|
|
312
|
152 |
|
if (!isset(self::$resolvers[$class . $opt])) { |
313
|
66 |
|
self::$resolvers[$class . $opt] = new Common\OptionsResolver(); |
314
|
66 |
|
$this->configureOptions(self::$resolvers[$class . $opt]); |
315
|
|
|
} |
316
|
|
|
|
317
|
152 |
|
return self::$resolvers[$class . $opt]->resolve($data); |
318
|
|
|
} |
319
|
|
|
|
320
|
|
|
/** |
321
|
|
|
* Change key in entry to different one in accordance with import root config |
322
|
|
|
* |
323
|
|
|
* @param string $defaultKey |
324
|
|
|
* |
325
|
|
|
* @return string |
326
|
|
|
*/ |
327
|
46 |
|
protected function getChildKey(string $defaultKey): string |
328
|
|
|
{ |
329
|
46 |
|
if (!empty($this->getImportRoot()) && $this->directionAsResponse) { |
|
|
|
|
330
|
1 |
|
return $this->getImportRoot(); |
331
|
|
|
} |
332
|
45 |
|
return $defaultKey; |
333
|
|
|
} |
334
|
|
|
|
335
|
|
|
/** |
336
|
|
|
* Change namespace prefix to different one in accordance with import root config |
337
|
|
|
* |
338
|
|
|
* @param string $defaultPrefix |
339
|
|
|
* |
340
|
|
|
* @return string |
341
|
|
|
*/ |
342
|
46 |
|
protected function getChildNamespacePrefix(string $defaultPrefix): string |
343
|
|
|
{ |
344
|
46 |
|
if (!empty($this->getImportRoot()) && $this->directionAsResponse) { |
|
|
|
|
345
|
1 |
|
list($prefix, ) = explode(':', $this->getImportRoot()); |
346
|
1 |
|
return $prefix; |
347
|
|
|
} |
348
|
45 |
|
return $defaultPrefix; |
349
|
|
|
} |
350
|
|
|
|
351
|
|
|
} |
352
|
|
|
|
This check looks for function or method calls that always return null and whose return value is used.
The method
getObject()
can return nothing but null, so it makes no sense to use the return value.The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.