From c9d48e2aa921566bc104e9b9cc2cdd677a72cf3a Mon Sep 17 00:00:00 2001 From: Jacek Pyziak Date: Tue, 16 Sep 2025 14:44:15 +0200 Subject: [PATCH] =?UTF-8?q?Dodaj=20skrypt=20do=20konwersji=20CSV=20na=20XM?= =?UTF-8?q?L=20z=20obs=C5=82ug=C4=85=20HTTP=20i=20CLI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- interblue.pl/sollux.php | 196 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 interblue.pl/sollux.php diff --git a/interblue.pl/sollux.php b/interblue.pl/sollux.php new file mode 100644 index 0000000..9806883 --- /dev/null +++ b/interblue.pl/sollux.php @@ -0,0 +1,196 @@ + 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(''); + +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); \ No newline at end of file