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

180 lines
5.7 KiB
PHP

<?php
declare(strict_types=1);
namespace KupShop\FeedsBundle\Formatter;
use KupShop\FeedsBundle\Dto\FeedItem;
class StreamedCSVFormatter implements StreamedFormatterInterface
{
public static function getType(): string
{
return 'csv';
}
public static function getContentType(): string
{
return 'text/csv';
}
/**
* @param \Generator<int, FeedItem> $data
*/
public function process(array $configuration, \Generator $data): void
{
$file = new \SplFileObject('php://output', 'w');
// backward compatibility with old CsvFormat
$separator = $configuration['separator'] ?? ',';
$header = $configuration['header'] ?? false;
$enclosureCustom = $configuration['enclosure-custom'] ?? false;
$enclosure = $configuration['enclosure'] ?? '"';
$escape = $configuration['escape'] ?? '\\';
if ($timestamp = $configuration['timestamp'] ?? false) {
$file->fwrite('# timestamp '.$timestamp.PHP_EOL);
}
foreach ($this->getFlattenedGenerator($configuration, $data) as $key => $item) {
if (empty($item)) {
continue;
}
// add header
if ($header && $key === 0) {
if ($enclosureCustom) {
$file->fwrite(implode($separator, array_map([$this, 'encodeValue'], array_keys($item)))."\r\n");
} else {
$file->fputcsv(array_keys($item), $separator, $enclosure, $escape);
}
}
// add data row
if ($enclosureCustom) {
$file->fwrite(implode($separator, $item)."\r\n");
} else {
$file->fputcsv($item, $separator, $enclosure, $escape);
}
}
}
protected function getFlattenedGenerator(array $configuration, \Generator $data): \Generator
{
foreach ($data as $item) {
yield $this->prepareItem(
$configuration,
$item,
$this->toFlattenArray($item->asSimpleArray())
);
}
}
/**
* Converts multi-dimensional array to simple flatten array.
*/
private function toFlattenArray(array $data): array
{
$flattened = [];
foreach ($data as $key => $value) {
if (is_array($value)) {
$flattened[$key] = $this->formatArrayAsMultilineString($value);
} else {
$flattened[$key] = $value === null ? '' : $value;
}
}
return $flattened;
}
private function formatArrayAsMultilineString(array $array): string
{
$formatted = [];
foreach ($array as $item) {
if (is_array($item)) {
$lines = [];
foreach ($item as $value) {
$lines[] = is_array($value) ? $this->formatArrayAsMultilineString($value) : $value;
}
$formatted[] = implode("\n\n", $lines);
} else {
$formatted[] = $item;
}
}
return implode("\n", $formatted);
}
/**
* Prepares item by its configuration in attributes.
*/
private function prepareItem(array $configuration, FeedItem $original, array $item): array
{
$newItem = [];
$enclosureCustom = $configuration['enclosure-custom'] ?? false;
foreach ($item as $key => $value) {
$attributes = $original->getAttributes($key);
// replace key by `override-name` attribute
if (!empty($attributes['override-name'])) {
$key = $attributes['override-name'];
}
$forceEnclosure = $attributes['enclosure'] ?? false;
$doubleQuotesWithoutEscaping = $attributes['double-quotes-without-escaping'] ?? false;
$tripleQuotesWithoutEscaping = $attributes['triple-quotes-without-escaping'] ?? false;
if ($enclosureCustom && ($forceEnclosure || $forceEnclosure === '') && !empty($value)) {
if ($doubleQuotesWithoutEscaping || $doubleQuotesWithoutEscaping === '') {
$value = $this->doubleQuotesWithoutEscaping($value);
} elseif ($tripleQuotesWithoutEscaping || $tripleQuotesWithoutEscaping === '') {
$value = $this->tripleQuotesWithoutEscaping($value);
} else {
$value = $this->encodeValue($value);
}
}
$newItem[$key] = $value;
}
return $newItem;
}
protected function doubleQuotesWithoutEscaping(string $value): string
{
// remove any ESCAPED double quotes within string.
$value = str_replace('\\"', '"', $value);
// then replace double quotes with two double quotes
$value = str_replace('"', '""', $value);
// force wrap value in quotes and return
return '"'.$value.'"';
}
private function tripleQuotesWithoutEscaping(string $value): string
{
// remove any ESCAPED double quotes within string.
$value = str_replace('\\"', '"', $value);
// then replace double quotes with triple double quotes
$value = str_replace('"', '"""', $value);
// force wrap value in quotes and return
return '"'.$value.'"';
}
private function encodeValue(string $value): string
{
// remove any ESCAPED double quotes within string.
$value = str_replace('\\"', '"', $value);
// then force escape these same double quotes And Any UNESCAPED Ones.
$value = str_replace('"', '\"', $value);
// force wrap value in quotes and return
return '"'.$value.'"';
}
}