174 lines
4.7 KiB
PHP
174 lines
4.7 KiB
PHP
<?php
|
|
namespace nulib\file\tab;
|
|
|
|
use DateTimeInterface;
|
|
use nulib\cl;
|
|
use nulib\file\TempStream;
|
|
use nulib\os\path;
|
|
use nulib\php\nur_func;
|
|
use nulib\php\time\DateTime;
|
|
use nulib\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, ?array $rowStyle=null): 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, ?array $rowStyle=null): void {
|
|
$row = $this->cookRow($row);
|
|
if ($row === null) return;
|
|
$this->writeHeaders(array_keys($row));
|
|
$this->_write($row, $rowStyle);
|
|
}
|
|
|
|
function writeAll(?iterable $rows=null, ?array $rowStyle=null): void {
|
|
$unsetRows = false;
|
|
if ($rows === null) {
|
|
$rows = $this->rows;
|
|
$unsetRows = true;
|
|
}
|
|
if ($rows !== null) {
|
|
foreach ($rows as $row) {
|
|
$this->write(cl::with($row), $rowStyle);
|
|
}
|
|
}
|
|
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();
|
|
}
|
|
}
|