Skip to content

Instantly share code, notes, and snippets.

@tezvi
Last active August 2, 2023 11:57
Show Gist options
  • Save tezvi/a598b98eeaad1701a4b65bee96953295 to your computer and use it in GitHub Desktop.
Save tezvi/a598b98eeaad1701a4b65bee96953295 to your computer and use it in GitHub Desktop.
CSV reader class that supports mapping columns with custom names.
<?php
/**
* CSV reader class that supports mapping columns with custom names.
*
* @author Andrej Vitez <andrejvitez@gmail.com>
*/
class CsvReader implements Iterator {
private string $filePath;
private array $columns = [];
private SplFileObject $file;
private ?array $currentRow = null;
/**
* @param string $filePath CSV file path.
* @param array $columns Array of column names indexed by key names.
* @param bool $firstRowHeaders If true first row will be skipped from iteration.
* @param string $delimiter Delimiter to be used when parsing CSV, default is semicolon.
* @param string $enclosure Character that encapsulates column value, default is double quote.
* @param string $escape Character used for escaping sequence, default is backslash.
*/
public function __construct(
string $filePath,
array $columns = [],
bool $firstRowHeaders = false,
string $delimiter = ';',
string $enclosure = '"',
string $escape = '\\'
) {
if (!is_readable($filePath)) {
throw new RuntimeException("CSV file '$filePath' is not readable");
}
$this->filePath = $filePath;
$this->columns = $columns;
$this->firstRowHeaders = $firstRowHeaders;
$this->file = new SplFileObject($this->filePath, 'r');
$this->file->setFlags(SplFileObject::READ_CSV);
$this->file->setCsvControl($delimiter, $enclosure, $escape);
$this->file->setFlags(SplFileObject::READ_CSV | SplFileObject::READ_AHEAD | SplFileObject::SKIP_EMPTY);
}
public function setColumnNames(array $columns): void {
$this->columns = $columns;
}
public function key(): ?int {
return $this->file->key();
}
public function rewind(): void {
$this->file->rewind();
$this->readRow();
if ($this->firstRowHeaders) {
$this->readRow();
}
}
private function readRow(): void {
$row = $this->file->current();
if (count($this->columns)) {
$this->currentRow = array_combine($this->columns, $row);
} else {
$this->currentRow = $row;
}
$this->file->next();
}
public function current(): ?array {
return $this->currentRow;
}
public function next(): void {
$this->readRow();
}
public function valid(): bool {
while ($this->currentRow !== false && $this->isEmptyRow()) {
$this->next();
}
return $this->currentRow !== false;
}
private function isEmptyRow(): bool {
foreach ($this->currentRow as $cell) {
if (!empty(trim($cell))) {
return false;
}
}
return true;
}
public function __destruct() {
$this->close();
}
public function close(): void {
unset($this->file);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment