Dodaj skrypt do konwersji CSV na XML z obsługą HTTP i CLI

This commit is contained in:
2025-09-16 14:44:15 +02:00
parent 28321496e7
commit c9d48e2aa9

196
interblue.pl/sollux.php Normal file
View File

@@ -0,0 +1,196 @@
<?php
/**
* csv2xml.php
* Konwersja CSV (z separatorem ;) -> XML.
* Obsługa: HTTP (GET url=… [&filename=…]) lub CLI (php csv2xml.php URL [OUTPUT.xml])
*/
declare(strict_types=1);
$_GET['url'] = 'https://makeyourlight.com/uploads/feed/myl_produkty_polski.csv';
$_GET['filename'] = 'produkty.xml';
$_GET['download'] = 0;
$isCli = (php_sapi_name() === 'cli');
if ($isCli) {
if ($argc < 2) {
fwrite(STDERR, "Użycie: php {$argv[0]} URL [plik_wyjściowy.xml]\n");
exit(1);
}
$csvUrl = $argv[1];
$outName = $argv[2] ?? 'output.xml';
$download = false;
} else {
if (!isset($_GET['url']) || !filter_var($_GET['url'], FILTER_VALIDATE_URL)) {
http_response_code(400);
echo "Parametr ?url= jest wymagany i musi być poprawnym URL-em.";
exit;
}
$csvUrl = $_GET['url'];
$outName = isset($_GET['filename']) && preg_match('/^[\w\-.]+$/', $_GET['filename'])
? $_GET['filename']
: 'produkty.xml';
$download = isset($_GET['download']) && $_GET['download'] == '1';
}
// ─────────────────────────────────────────────────────────────────────────────
// Pobierz CSV
// ─────────────────────────────────────────────────────────────────────────────
$csvRaw = @file_get_contents($csvUrl);
if ($csvRaw === false) {
$err = error_get_last();
$msg = $err['message'] ?? 'Nieznany błąd pobierania CSV.';
if ($isCli) fwrite(STDERR, "Błąd pobierania CSV: $msg\n");
else { http_response_code(502); echo "Błąd pobierania CSV: $msg"; }
exit(1);
}
$csvRaw = preg_replace('/^\xEF\xBB\xBF/', '', $csvRaw);
// ─────────────────────────────────────────────────────────────────────────────
// Parsowanie CSV
// ─────────────────────────────────────────────────────────────────────────────
$lines = preg_split("/\r\n|\n|\r/", $csvRaw);
$rows = [];
foreach ($lines as $line) {
if (trim($line) === '') continue;
$parsed = str_getcsv($line, ';', '"', "\\");
if (is_array($parsed)) $rows[] = $parsed;
}
if (count($rows) < 2) {
$msg = "CSV nie zawiera danych (brakuje nagłówków lub wierszy).";
if ($isCli) fwrite(STDERR, $msg . PHP_EOL);
else { http_response_code(422); echo $msg; }
exit(1);
}
$headers = array_map('trim', $rows[0]);
$dataRows = array_slice($rows, 1);
// ─────────────────────────────────────────────────────────────────────────────
// Helpery
// ─────────────────────────────────────────────────────────────────────────────
function normalize_tag_name(string $name): string {
$map = [
'ą'=>'a','ć'=>'c','ę'=>'e','ł'=>'l','ń'=>'n','ó'=>'o','ś'=>'s','ż'=>'z','ź'=>'z',
'Ą'=>'A','Ć'=>'C','Ę'=>'E','Ł'=>'L','Ń'=>'N','Ó'=>'O','Ś'=>'S','Ż'=>'Z','Ź'=>'Z',
];
$name = strtr($name, $map);
$name = preg_replace('/[^\p{L}\p{N}\-_ ]/u', ' ', $name);
$name = preg_replace('/\s+/', '_', $name);
$name = preg_replace('/[^\p{L}\p{N}_-]/u', '_', $name);
$name = preg_replace('/_+/', '_', $name);
$name = trim($name, '_');
if ($name === '') $name = 'field';
if (preg_match('/^\d/', $name)) $name = 'f_' . $name;
return $name;
}
function addChildWithCDATA(SimpleXMLElement $parent, string $name, string $value): SimpleXMLElement {
$child = $parent->addChild($name);
$node = dom_import_simplexml($child);
if ($node) $node->appendChild($node->ownerDocument->createCDATASection($value));
return $child;
}
function looks_like_html(string $v): bool {
return (bool) preg_match('/<[^>]+>|\&(?:nbsp|lt|gt|amp|quot|#\d+);/i', $v);
}
function is_photo_col(string $header): bool {
return (bool) preg_match('/^Photo\d+$/i', trim($header));
}
// Mapa tagów
$tagMap = [];
foreach ($headers as $h) $tagMap[$h] = normalize_tag_name($h);
// ─────────────────────────────────────────────────────────────────────────────
// Budowa XML
// ─────────────────────────────────────────────────────────────────────────────
$xml = new SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><Products/>');
foreach ($dataRows as $row) {
if (count($row) < count($headers)) $row = array_pad($row, count($headers), '');
$product = $xml->addChild('Product');
$photos = [];
foreach ($headers as $i => $header) {
$value = isset($row[$i]) ? trim($row[$i]) : '';
$tagName = $tagMap[$header] ?? 'field_' . $i;
if (is_photo_col($header)) { if ($value !== '') $photos[] = $value; continue; }
if ($value === '') { $product->addChild($tagName, ''); continue; }
if (looks_like_html($value) || preg_match('/opis/i', $header)) {
addChildWithCDATA($product, $tagName, $value);
} else {
$product->addChild($tagName, $value);
}
}
if (!empty($photos)) {
$photosNode = $product->addChild('Photos');
foreach ($photos as $p) $photosNode->addChild('Photo', $p);
}
}
// Formatowanie DOM
$dom = new DOMDocument('1.0', 'UTF-8');
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->loadXML($xml->asXML());
// ─────────────────────────────────────────────────────────────────────────────
// Zapis wyniku
// ─────────────────────────────────────────────────────────────────────────────
if ($isCli) {
if (@$dom->save($outName) === false) {
fwrite(STDERR, "Nie udało się zapisać do pliku: {$outName}\n");
exit(1);
}
echo "Zapisano XML do: {$outName}\n";
exit(0);
}
// HTTP — zapis do exports/
$saveDirFs = __DIR__ . DIRECTORY_SEPARATOR . 'exports';
if (!is_dir($saveDirFs)) {
// spróbuj utworzyć katalog rekurencyjnie
if (!@mkdir($saveDirFs, 0775, true) && !is_dir($saveDirFs)) {
http_response_code(500);
echo "Nie można utworzyć katalogu zapisu: {$saveDirFs}";
exit;
}
}
// sanity check nazwy pliku
$outName = preg_match('/^[\w\-.]+$/', $outName) ? $outName : ('produkty_' . date('Ymd_His') . '.xml');
$savePathFs = $saveDirFs . DIRECTORY_SEPARATOR . $outName;
if (@$dom->save($savePathFs) === false) {
http_response_code(500);
echo "Nie udało się zapisać pliku: {$savePathFs}";
exit;
}
// Zbuduj publiczny URL (relatywny do skryptu)
$basePath = rtrim(str_replace('\\', '/', dirname($_SERVER['SCRIPT_NAME'] ?? '/')), '/');
$publicRel = $basePath . '/exports/' . rawurlencode($outName);
$scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
$host = $_SERVER['HTTP_HOST'] ?? '';
$publicUrl = $host ? ($scheme . '://' . $host . $publicRel) : $publicRel;
// Jeśli proszono o pobranie — wyślij plik
if ($download) {
header('Content-Type: application/xml; charset=UTF-8');
header('Content-Disposition: attachment; filename="'.$outName.'"');
readfile($savePathFs);
exit;
}
// Domyślnie zwróć prosty JSON z informacją o zapisie
header('Content-Type: application/json; charset=UTF-8');
echo json_encode([
'status' => 'ok',
'message' => 'Plik XML zapisany na serwerze.',
'file_path' => $savePathFs,
'file_url' => $publicUrl,
'filename' => $outName,
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);