174 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			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();
 | 
						|
  }
 | 
						|
}
 |