nur-sery/src/file/csv/AbstractBuilder.php

174 lines
4.6 KiB
PHP

<?php
namespace nur\sery\file\csv;
use DateTimeInterface;
use nur\sery\cl;
use nur\sery\file\TempStream;
use nur\sery\os\path;
use nur\sery\php\nur_func;
use nur\sery\php\time\DateTime;
use nur\sery\web\http;
abstract class AbstractBuilder extends TempStream implements IBuilder {
/** @var ?array schéma des données à écrire */
const SCHEMA = null;
/** @var ?array liste des colonnes à écrire */
const HEADERS = null;
/**
* @var bool faut-il écrire les en-têtes, soit celles qui sont spécifiées,
* soit celles qui sont calculées à partir des clés de la première ligne
*/
const USE_HEADERS = true;
/** @var ?string nom du fichier téléchargé */
const OUTPUT = null;
function __construct(?string $output, ?array $params=null) {
if ($output !== null) $params["output"] = $output;
$this->schema = $params["schema"] ?? static::SCHEMA;
$this->headers = $params["headers"] ?? static::HEADERS;
$this->useHeaders = $params["use_headers"] ?? static::USE_HEADERS;
$rows = $params["rows"] ?? null;
if (is_callable($rows)) $rows = $rows();
$this->rows = $rows;
$cookFunc = $params["cook_func"] ?? null;
$cookCtx = $cookArgs = null;
if ($cookFunc !== null) {
nur_func::ensure_func($cookFunc, $this, $cookArgs);
$cookCtx = nur_func::_prepare($cookFunc);
}
$this->cookCtx = $cookCtx;
$this->cookArgs = $cookArgs;
$this->output = $params["output"] ?? static::OUTPUT;
$maxMemory = $params["max_memory"] ?? null;
$throwOnError = $params["throw_on_error"] ?? null;
parent::__construct($maxMemory, $throwOnError);
}
protected ?array $schema;
protected ?array $headers;
protected bool $useHeaders;
protected ?iterable $rows;
protected ?string $output;
protected ?array $cookCtx;
protected ?array $cookArgs;
protected function ensureHeaders(?array $row=null): void {
if ($this->headers !== null || !$this->useHeaders) return;
if ($this->schema === null) $headers = null;
else $headers = array_keys($this->schema);
if ($headers === null && $row !== null) $headers = array_keys($row);
$this->headers = $headers;
}
protected abstract function _write(array $row): void;
protected bool $wroteHeaders = false;
function writeHeaders(?array $headers=null): void {
if ($this->wroteHeaders) return;
if ($this->useHeaders) {
if ($headers !== null) $this->headers = $headers;
else $this->ensureHeaders();
if ($this->headers !== null) $this->_write($this->headers);
}
$this->wroteHeaders = true;
}
protected function cookRow(?array $row): ?array {
if ($this->cookCtx !== null) {
$args = cl::merge([$row], $this->cookArgs);
$row = nur_func::_call($this->cookCtx, $args);
}
if ($row !== null) {
foreach ($row as &$value) {
# formatter les dates
if ($value instanceof DateTime) {
$value = $value->format();
} elseif ($value instanceof DateTimeInterface) {
$value = DateTime::with($value)->format();
}
}; unset($value);
}
return $row;
}
function write(?array $row): void {
$row = $this->cookRow($row);
if ($row === null) return;
$this->writeHeaders(array_keys($row));
$this->_write($row);
}
function writeAll(?iterable $rows=null): void {
$unsetRows = false;
if ($rows === null) {
$rows = $this->rows;
$unsetRows = true;
}
if ($rows !== null) {
foreach ($rows as $row) {
$this->write(cl::with($row));
}
}
if ($unsetRows) $this->rows = null;
}
abstract protected function _sendContentType(): void;
protected bool $sentHeaders = false;
function sendHeaders(): void {
if ($this->sentHeaders) return;
$this->_sendContentType();
$output = $this->output;
if ($output !== null) {
http::download_as(path::filename($output));
}
$this->sentHeaders = true;
}
protected function _build(?iterable $rows=null): void {
$this->writeAll($rows);
$this->writeHeaders();
}
abstract protected function _checkOk(): bool;
protected bool $built = false, $closed = false;
function build(?iterable $rows=null, bool $close=true): bool {
$ok = true;
if (!$this->built) {
$this->_build($rows);
$this->built = true;
}
if ($close && !$this->closed) {
$ok = $this->_checkOk();
$this->closed = true;
}
return $ok;
}
function sendFile(?iterable $rows=null): int {
if (!$this->built) {
$this->_build($rows);
$this->built = true;
}
if (!$this->closed) {
if (!$this->_checkOk()) return 0;
$this->closed = true;
}
$this->sendHeaders();
return $this->fpassthru();
}
}