Files
kupshop/bundles/KupShop/FeedsBundle/Utils/FeedRenderer.php
2025-08-02 16:30:27 +02:00

138 lines
4.3 KiB
PHP

<?php
declare(strict_types=1);
namespace KupShop\FeedsBundle\Utils;
use KupShop\FeedsBundle\Dto\FeedItem;
use KupShop\FeedsBundle\Feed\IFeed;
class FeedRenderer
{
public function __construct(
private FeedUtils $feedUtils,
) {
}
public function render(IFeed $feed, array $feedRow, ?int $limit = null, string $format = 'xml'): void
{
// standard rendering of XML feed
if ($format == 'xml') {
$feed->render($feedRow, $limit);
return;
}
// we need generator that can be passed to formatter
$generator = function () use ($feed, $feedRow, $limit): \Generator {
foreach ($feed->renderWithGenerator($feedRow, $limit) as $key => $xmlItem) {
// ignoring XML root element, only inner items are passed to formatter
if (in_array($key, ['start', 'end'])) {
continue;
}
foreach ($this->getXMLAsArray($xmlItem) as $item) {
yield $item;
}
}
};
$formatter = $this->feedUtils->getFeedFormatter($format);
$formatter->process(
$this->getConfiguration($feedRow),
$generator()
);
}
private function getConfiguration(array $feedRow): array
{
$template = json_decode($feedRow['template'] ?: '', true) ?: [];
$xml = new \SimpleXMLElement($template['beginning'].$template['end']);
$attributes = [];
foreach ($xml->attributes() as $key => $value) {
$attributes[$key] = (string) $value;
}
return $attributes;
}
/**
* @return FeedItem[]
*/
private function getXMLAsArray(string $xml): array
{
$reader = new \XMLReader();
$reader->xml("<root>{$xml}</root>");
// move to the first non-root element
while (($reader->nodeType !== \XMLReader::ELEMENT || $reader->name === 'root') && $reader->read()) {
}
$elementName = $reader->name;
$result = [];
// iterate each element inside of root element and convert it to array
do {
if ($reader->nodeType === \XMLReader::ELEMENT && $reader->name === $elementName) {
$result[] = new FeedItem($this->parseXMLNode($reader));
}
} while ($reader->read());
$reader->close();
return $result;
}
private function parseXMLNode(\XMLReader $reader): array|string|null
{
$data = [];
$currentElement = $reader->name;
while ($reader->read()) {
if ($reader->nodeType == \XMLReader::ELEMENT) {
$key = $reader->name;
// Extract attributes
$attributes = [];
if ($reader->hasAttributes) {
while ($reader->moveToNextAttribute()) {
$attributes[$reader->name] = $reader->value;
}
$reader->moveToElement(); // Move back to the element
}
if (!$reader->isEmptyElement) {
// recursively process the child node
$value = $this->parseXMLNode($reader);
} else {
$value = null; // handle empty elements
}
$elementData = ['@value' => $value];
if (!empty($attributes)) {
$elementData['@attributes'] = $attributes;
}
// assign to the data array
if (isset($data[$key])) {
if (!is_array($data[$key]) || array_keys($data[$key]) !== range(0, count($data[$key]) - 1)) {
$data[$key] = [$data[$key]]; // Convert to array if not already
}
$data[$key][] = $elementData;
} else {
$data[$key] = $elementData;
}
} elseif ($reader->nodeType == \XMLReader::TEXT || $reader->nodeType == \XMLReader::CDATA) {
return htmlspecialchars_decode($reader->value, ENT_QUOTES);
} elseif ($reader->nodeType == \XMLReader::END_ELEMENT && $reader->name == $currentElement) {
break;
}
}
return empty($data) ? null : $data;
}
}