*/
function repeatingElements(Reader $reader, string $childElementName): array
{
if ('{' !== $childElementName[0]) {
$childElementName = '{}'.$childElementName;
}
$result = [];
foreach ($reader->parseGetElements() as $element) {
if ($element['name'] === $childElementName) {
$result[] = $element['value'];
}
}
return $result;
}
/**
* This deserializer helps you to deserialize structures which contain mixed content.
*
* some text and an inline tagand even more text
*
* The above example will return
*
* [
* 'some text',
* [
* 'name' => '{}extref',
* 'value' => 'and an inline tag',
* 'attributes' => []
* ],
* 'and even more text'
* ]
*
* In strict XML documents you won't find this kind of markup but in html this is a quite common pattern.
*
* @return array
*/
function mixedContent(Reader $reader): array
{
// If there's no children, we don't do anything.
if ($reader->isEmptyElement) {
$reader->next();
return [];
}
$previousDepth = $reader->depth;
$content = [];
$reader->read();
while (true) {
if (Reader::ELEMENT == $reader->nodeType) {
$content[] = $reader->parseCurrentElement();
} elseif ($reader->depth >= $previousDepth && in_array($reader->nodeType, [Reader::TEXT, Reader::CDATA, Reader::WHITESPACE])) {
$content[] = $reader->value;
$reader->read();
} elseif (Reader::END_ELEMENT == $reader->nodeType) {
// Ensuring we are moving the cursor after the end element.
$reader->read();
break;
} else {
$reader->read();
}
}
return $content;
}
/**
* The functionCaller deserializer turns an XML element into whatever your callable returns.
*
* You can use, e.g., a named constructor (factory method) to create an object using
* this function.
*
* @return mixed whatever the 'func' callable returns
*
* @throws \InvalidArgumentException|\ReflectionException
*/
function functionCaller(Reader $reader, callable $func, string $namespace)
{
if ($reader->isEmptyElement) {
$reader->next();
return null;
}
$funcArgs = [];
if (is_array($func)) {
$ref = new \ReflectionMethod($func[0], $func[1]);
} elseif (is_string($func) && false !== strpos($func, '::')) {
// We have a string that should refer to a method that exists, like "MyClass::someMethod"
// ReflectionMethod knows how to handle that as-is
$ref = new \ReflectionMethod($func);
} elseif ($func instanceof \Closure || is_string($func)) {
// We have an actual Closure (a real function) or a string that is the name of a function
// ReflectionFunction can take either of those
$ref = new \ReflectionFunction($func);
} else {
throw new \InvalidArgumentException(__METHOD__.' unable to use func parameter with ReflectionMethod or ReflectionFunction.');
}
foreach ($ref->getParameters() as $parameter) {
$funcArgs[$parameter->getName()] = null;
}
$reader->read();
do {
if (Reader::ELEMENT === $reader->nodeType && $reader->namespaceURI == $namespace) {
if (array_key_exists($reader->localName, $funcArgs)) {
$funcArgs[$reader->localName] = $reader->parseCurrentElement()['value'];
} else {
// Ignore property
$reader->next();
}
} else {
$reader->read();
}
} while (Reader::END_ELEMENT !== $reader->nodeType);
$reader->read();
return $func(...array_values($funcArgs));
}