222 lines
4.4 KiB
PHP
222 lines
4.4 KiB
PHP
<?php
|
|
|
|
class stTaskSchedulerImportCsvReader implements stTaskSchedulerImportReaderInterface
|
|
{
|
|
/**
|
|
*
|
|
* @var resource|null
|
|
*/
|
|
protected $fh = null;
|
|
|
|
/**
|
|
*
|
|
* @var null|int
|
|
*/
|
|
protected $count = null;
|
|
|
|
/**
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $separator;
|
|
|
|
/**
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $encoding;
|
|
|
|
/**
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $enclosure;
|
|
|
|
/**
|
|
*
|
|
* @var bool
|
|
*/
|
|
protected $readHeaderFields;
|
|
|
|
/**
|
|
* @var int|null
|
|
*/
|
|
protected $dataOffset = null;
|
|
|
|
/**
|
|
* @var array|null
|
|
*/
|
|
protected $fields = null;
|
|
|
|
public function __clone()
|
|
{
|
|
if (null !== $this->fh)
|
|
{
|
|
$this->fh = clone $this->fh;
|
|
}
|
|
}
|
|
|
|
public function __construct(string $separator = ";", string $encoding = "UTF-8", $enclosure = '"', bool $readHeaderFields = true)
|
|
{
|
|
$this->separator = $separator;
|
|
$this->encoding = $encoding;
|
|
$this->enclosure = $enclosure;
|
|
$this->readHeaderFields = $readHeaderFields;
|
|
}
|
|
|
|
public function __destruct()
|
|
{
|
|
$this->close();
|
|
}
|
|
|
|
public function getFiletypeExtension(): string
|
|
{
|
|
return 'csv';
|
|
}
|
|
|
|
public function open(string $file): bool
|
|
{
|
|
$this->fh = fopen($file, "r");
|
|
|
|
return false !== $this->fh;
|
|
}
|
|
|
|
public function isValid(): bool
|
|
{
|
|
try
|
|
{
|
|
$result = $this->read(function() {});
|
|
}
|
|
catch (stTaskSchedulerImportReaderException $e)
|
|
{
|
|
$result = false;
|
|
}
|
|
|
|
$this->rewind();
|
|
|
|
return $result;
|
|
}
|
|
|
|
public function rewind(): void
|
|
{
|
|
rewind($this->fh);
|
|
}
|
|
|
|
public function count(): int
|
|
{
|
|
if (null === $this->count)
|
|
{
|
|
fseek($this->fh, 0, SEEK_END);
|
|
$this->count = $this->getOffset();
|
|
$this->rewind();
|
|
}
|
|
|
|
return $this->count;
|
|
}
|
|
|
|
public function setOffset(int $offset): void
|
|
{
|
|
fseek($this->fh, $offset);
|
|
}
|
|
|
|
public function getOffset(): int
|
|
{
|
|
return ftell($this->fh);
|
|
}
|
|
|
|
public function read(\Closure $callback): bool
|
|
{
|
|
if ($this->readHeaderFields)
|
|
{
|
|
$fields = $this->getFields();
|
|
|
|
if (!$this->getOffset() && $this->dataOffset)
|
|
{
|
|
$this->setOffset($this->dataOffset);
|
|
}
|
|
}
|
|
|
|
$data = $this->getNextLine();
|
|
|
|
if ($data && $this->readHeaderFields)
|
|
{
|
|
$data = array_slice($data, 0, count($fields));
|
|
|
|
$combinedData = array_combine($fields, $data);
|
|
|
|
if (false === $combinedData)
|
|
{
|
|
throw new stTaskSchedulerImportReaderException(sprintf("The number of header fields is not equal to the number of read data fields (fields: \"%s\", data: \"%s\")",
|
|
implode('", "', $fields),
|
|
implode('", "', $data)
|
|
));
|
|
}
|
|
|
|
$data = $combinedData;
|
|
}
|
|
|
|
if ($data)
|
|
{
|
|
if ("UTF-8" != $this->encoding)
|
|
{
|
|
$data = array_map(function($value) {
|
|
return iconv($this->encoding, "UTF-8//TRANSLIT", $value);
|
|
}, $data);
|
|
}
|
|
|
|
$callback($data);
|
|
}
|
|
|
|
return !empty($data);
|
|
}
|
|
|
|
public function close(): void
|
|
{
|
|
if ($this->fh)
|
|
{
|
|
fclose($this->fh);
|
|
$this->fh = null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Czyta i zwraca następna linię z pliku csv
|
|
*
|
|
* @return array|null
|
|
*/
|
|
private function getNextLine(): ?array
|
|
{
|
|
$data = fgetcsv($this->fh, null, $this->separator, $this->enclosure, "\0");
|
|
|
|
return false !== $data ? array_map('trim', $data) : null;
|
|
}
|
|
|
|
/**
|
|
* Odczytuje nazwy pól
|
|
*
|
|
* @return array|null
|
|
*/
|
|
private function getFields(): ?array
|
|
{
|
|
$offset = $this->getOffset();
|
|
|
|
if (null === $this->fields)
|
|
{
|
|
if ($offset > 0)
|
|
{
|
|
$this->rewind();
|
|
}
|
|
|
|
$this->fields = $this->getNextLine();
|
|
$this->dataOffset = $this->getOffset();
|
|
|
|
if ($offset > 0)
|
|
{
|
|
$this->setOffset($offset);
|
|
}
|
|
}
|
|
|
|
return $this->fields;
|
|
}
|
|
}
|