release 1.0

This commit is contained in:
Reindl David (IT-PTR-CEN2-SL10)
2026-05-02 17:53:19 +02:00
parent b1cad04fdf
commit 170b2d2016
26 changed files with 5748 additions and 0 deletions
+183
View File
@@ -0,0 +1,183 @@
<?php
namespace UbsCsvTransformer;
/**
* Liest und parst CSV-Dateien
*
* Diese Klasse liest CSV-Dateien mit konfigurierbarem Delimiter
* und separiert Metadaten-Zeilen von den eigentlichen Datenzeilen.
*/
class CsvReader
{
private string $filePath;
private string $delimiter;
private int $headerLine;
private bool $hasBom;
/**
* @param string $filePath Pfad zur CSV-Datei
* @param array $csvStructure CSV-Struktur aus Konfiguration
*/
public function __construct(string $filePath, array $csvStructure)
{
$this->filePath = $filePath;
$this->delimiter = $csvStructure['inputDelimiter'] ?? ';';
$this->headerLine = $csvStructure['headerLine'] ?? 1;
$this->hasBom = $csvStructure['hasBom'] ?? false;
}
/**
* Liest alle Zeilen aus der Datei
*
* @param int $maxLines Maximale Anzahl Zeilen (0 = alle)
* @return array Array mit Zeilen (ohne Newlines)
* @throws \RuntimeException wenn Datei nicht gelesen werden kann
*/
public function readLines(int $maxLines = 0): array
{
if (!file_exists($this->filePath) || !is_readable($this->filePath)) {
throw new \RuntimeException("Konnte Datei nicht lesen: {$this->filePath}");
}
$lines = file($this->filePath, FILE_IGNORE_NEW_LINES);
if ($lines === false) {
throw new \RuntimeException("Konnte Datei nicht lesen: {$this->filePath}");
}
// BOM entfernen falls vorhanden
if ($this->hasBom && !empty($lines)) {
$lines[0] = $this->removeBom($lines[0]);
}
if ($maxLines > 0 && count($lines) > $maxLines) {
$lines = array_slice($lines, 0, $maxLines);
}
return $lines;
}
/**
* Liest die Metadaten-Zeilen (vor der Header-Zeile)
*
* @return array Array mit Metadaten-Zeilen
*/
public function readMetadataLines(): array
{
$lines = $this->readLines();
if ($this->headerLine <= 1) {
return [];
}
return array_slice($lines, 0, $this->headerLine - 1);
}
/**
* Liest die CSV-Daten mit Headers
*
* @param int $maxDataRows Maximale Anzahl Datenzeilen (0 = alle)
* @return array Array von assoziativen Arrays (mit Spalten-Namen als Keys)
* @throws \RuntimeException wenn Header-Zeile nicht gefunden
*/
public function readCsvData(int $maxDataRows = 0): array
{
$lines = $this->readLines();
if ($this->headerLine > count($lines)) {
throw new \RuntimeException("Header-Zeile {$this->headerLine} nicht gefunden in Datei mit " . count($lines) . " Zeilen");
}
// Header parsen
$headerLineContent = $lines[$this->headerLine - 1];
$headers = str_getcsv($headerLineContent, $this->delimiter, '"', '\\');
$headers = array_map(static fn(?string $v): string => trim($v ?? ''), $headers);
// Datenzeilen parsen
$data = [];
$dataStartLine = $this->headerLine; // 0-basiert
$lineCount = 0;
for ($i = $dataStartLine; $i < count($lines); $i++) {
if ($maxDataRows > 0 && $lineCount >= $maxDataRows) {
break;
}
$lineContent = $lines[$i];
// Leere Zeilen überspringen
if (trim($lineContent) === '') {
continue;
}
$row = str_getcsv($lineContent, $this->delimiter, '"', '\\');
$row = array_map(static fn(?string $v): string => trim($v ?? ''), $row);
// Zeile mit Header-Keys kombinieren
$rowData = [];
foreach ($headers as $index => $header) {
$rowData[$header] = $row[$index] ?? '';
}
$data[] = $rowData;
$lineCount++;
}
return $data;
}
/**
* Gibt die Spalten-Header zurück
*
* @return array Array mit Spalten-Namen
* @throws \RuntimeException wenn Header-Zeile nicht gefunden
*/
public function getHeaders(): array
{
$lines = $this->readLines();
if ($this->headerLine > count($lines)) {
throw new \RuntimeException("Header-Zeile {$this->headerLine} nicht gefunden");
}
$headerLineContent = $lines[$this->headerLine - 1];
$headers = str_getcsv($headerLineContent, $this->delimiter, '"', '\\');
return array_map(static fn(?string $v): string => trim($v ?? ''), $headers);
}
/**
* Entfernt UTF-8 BOM (Byte Order Mark) von String
*
* @param string $text String mit potenziellem BOM
* @return string String ohne BOM
*/
private function removeBom(string $text): string
{
if (str_starts_with($text, "\xEF\xBB\xBF")) {
return substr($text, 3);
}
return $text;
}
/**
* Gibt die Gesamtzahl der Zeilen in der Datei zurück
*
* @return int Anzahl Zeilen
*/
public function countLines(): int
{
return count($this->readLines());
}
/**
* Gibt die Anzahl der Datenzeilen zurück (ohne Header und Metadaten)
*
* @return int Anzahl Datenzeilen
*/
public function countDataRows(): int
{
return count($this->readCsvData());
}
}