modifs.mineures sans commentaires

This commit is contained in:
Jephté Clain 2024-06-14 06:36:05 +04:00
parent 907aba4590
commit db79a94b1d
6 changed files with 385 additions and 222 deletions

View File

@ -0,0 +1,114 @@
<?php
namespace nur\sery\ext\spreadsheet;
use nur\sery\file\csv\AbstractBuilder;
use nur\sery\file\csv\TAbstractBuilder;
use nur\sery\os\path;
use nur\sery\web\http;
use PhpOffice\PhpSpreadsheet\Cell\IValueBinder;
use PhpOffice\PhpSpreadsheet\Cell\StringValueBinder;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PhpOffice\PhpSpreadsheet\Writer\Ods;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
class PhpSpreadsheetBuilder extends AbstractBuilder {
use TAbstractBuilder;
/** @var string|int|null nom de la feuille dans laquelle écrire */
const WSNAME = null;
function __construct(?string $output, ?array $params=null) {
parent::__construct($output, $params);
$this->ss = new Spreadsheet();
$this->valueBinder = new StringValueBinder();
$this->setWsname($params["wsname"] ?? static::WSNAME);
}
protected Spreadsheet $ss;
protected IValueBinder $valueBinder;
protected ?Worksheet $ws;
protected int $nrow;
const STYLE_ROW = 0, STYLE_HEADER = 1;
protected int $rowStyle;
/**
* @param string|int|null $wsname
*/
function setWsname($wsname): self {
$ss = $this->ss;
$this->ws = null;
$this->nrow = 0;
$this->rowStyle = self::STYLE_ROW;
$ws = wsutils::get_ws($wsname, $ss);
if ($ws === null) {
$ws = $ss->createSheet()->setTitle($wsname);
$this->wroteHeaders = false;
} else {
$maxRow = wsutils::compute_max_coords($ws)[1];
$this->nrow = $maxRow - 1;
$this->wroteHeaders = $maxRow > 1;
}
$this->ws = $ws;
return $this;
}
function _write(array $row): void {
$ws = $this->ws;
$styleHeader = $this->rowStyle === self::STYLE_HEADER;
$nrow = ++$this->nrow;
$ncol = 1;
foreach ($row as $col) {
$ws->getCellByColumnAndRow($ncol++, $nrow)->setValue($col, $this->valueBinder);
}
if ($styleHeader) {
$ws->getStyle("$nrow:$nrow")->getFont()->setBold(true);
$maxcol = count($row);
for ($ncol = 1; $ncol <= $maxcol; $ncol++) {
$ws->getColumnDimensionByColumn($ncol)->setAutoSize(true);
}
}
}
function writeHeaders(?array $headers=null): void {
$this->rowStyle = self::STYLE_HEADER;
parent::writeHeaders($headers);
$this->rowStyle = self::STYLE_ROW;
}
function _sendContentType(): void {
switch (path::ext($this->output)) {
case ".ods":
$contentType = "application/vnd.oasis.opendocument.spreadsheet";
break;
case ".xlsx":
default:
$contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
break;
}
http::content_type($contentType);
}
function _sendFile(): int {
switch (path::ext($this->output)) {
case ".ods":
$writer = new Ods($this->ss);
break;
case ".xlsx":
default:
$writer = new Xlsx($this->ss);
break;
}
$this->rewind();
$writer->save($this->getResource());
$this->rewind();
$this->sendHeaders();
return $this->fpassthru();
}
}

View File

@ -0,0 +1,117 @@
<?php
namespace nur\sery\ext\spreadsheet;
use nur\sery\cl;
use nur\sery\file\csv\AbstractReader;
use nur\sery\file\csv\TAbstractReader;
use PhpOffice\PhpSpreadsheet\Cell\DataType;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\RichText\RichText;
class PhpSpreadsheetReader extends AbstractReader {
const DATETIME_FORMAT = 'dd/mm/yyyy hh:mm:ss';
const DATE_FORMAT = 'dd/mm/yyyy';
const TIME_FORMAT = 'hh:mm:ss';
const FORMAT_MAPPINGS = [
'mm/dd hh' => self::DATETIME_FORMAT,
'dd/mm hh' => self::DATETIME_FORMAT,
'mm/dd hh:mm' => self::DATETIME_FORMAT,
'dd/mm hh:mm' => self::DATETIME_FORMAT,
'mm/dd hh:mm:ss' => self::DATETIME_FORMAT,
'dd/mm hh:mm:ss' => self::DATETIME_FORMAT,
'mm/dd/yyyy hh' => self::DATETIME_FORMAT,
'dd/mm/yyyy hh' => self::DATETIME_FORMAT,
'mm/dd/yyyy hh:mm' => self::DATETIME_FORMAT,
'dd/mm/yyyy hh:mm' => self::DATETIME_FORMAT,
'mm/dd/yyyy hh:mm:ss' => self::DATETIME_FORMAT,
'dd/mm/yyyy hh:mm:ss' => self::DATETIME_FORMAT,
'yyyy/mm/dd hh' => self::DATETIME_FORMAT,
'yyyy/mm/dd hh:mm' => self::DATETIME_FORMAT,
'yyyy/mm/dd hh:mm:ss' => self::DATETIME_FORMAT,
'mm/dd' => self::DATE_FORMAT,
'dd/mm' => self::DATE_FORMAT,
'mm/dd/yyyy' => self::DATE_FORMAT,
'dd/mm/yyyy' => self::DATE_FORMAT,
'yyyy/mm/dd' => self::DATE_FORMAT,
'mm/yyyy' => self::DATE_FORMAT,
'hh AM/PM' => self::TIME_FORMAT,
'hh:mm AM/PM' => self::TIME_FORMAT,
'hh:mm:ss AM/PM' => self::TIME_FORMAT,
'hh' => self::TIME_FORMAT,
'hh:mm' => self::TIME_FORMAT,
'hh:mm:ss' => self::TIME_FORMAT,
'[hh]:mm:ss' => self::TIME_FORMAT,
'mm:ss' => self::TIME_FORMAT,
];
/** @var string|int|null nom de la feuille depuis laquelle lire */
const WSNAME = null;
function __construct($input, ?array $params=null) {
parent::__construct($input, $params);
$this->wsname = $params["wsname"] ?? static::WSNAME;
}
protected $wsname;
/**
* @param string|int|null $wsname
*/
function setWsname($wsname): self {
$this->wsname = $wsname;
return $this;
}
function getIterator() {
$ss = IOFactory::load($this->input);
$ws = wsutils::get_ws($this->wsname, $ss);
[$nbCols, $nbRows] = wsutils::compute_max_coords($ws);
$this->isrc = $this->idest = 0;
for ($nrow = 1; $nrow <= $nbRows; $nrow++) {
$row = [];
for ($ncol = 1; $ncol <= $nbCols; $ncol++) {
if ($ws->cellExistsByColumnAndRow($ncol, $nrow)) {
$cell = $ws->getCellByColumnAndRow($ncol, $nrow);
$col = $cell->getValue();
if ($col instanceof RichText) {
$col = $col->getPlainText();
} else {
$dataType = $cell->getDataType();
if ($dataType == DataType::TYPE_NUMERIC || $dataType == DataType::TYPE_FORMULA) {
# si c'est un format date, le forcer à une valeur standard
$origFormatCode = $cell->getStyle()->getNumberFormat()->getFormatCode();
if (strpbrk($origFormatCode, "ymdhs") !== false) {
$formatCode = $origFormatCode;
$formatCode = preg_replace('/y+/', "yyyy", $formatCode);
$formatCode = preg_replace('/m+/', "mm", $formatCode);
$formatCode = preg_replace('/d+/', "dd", $formatCode);
$formatCode = preg_replace('/h+/', "hh", $formatCode);
$formatCode = preg_replace('/s+/', "ss", $formatCode);
$formatCode = preg_replace('/-+/', "/", $formatCode);
$formatCode = preg_replace('/\\\\ /', " ", $formatCode);
$formatCode = preg_replace('/;@$/', "", $formatCode);
$formatCode = cl::get(self::FORMAT_MAPPINGS, $formatCode, $formatCode);
if ($formatCode !== $origFormatCode) {
$cell->getStyle()->getNumberFormat()->setFormatCode($formatCode);
}
}
}
$col = $cell->getFormattedValue();
$this->verifixCol($col);
}
} else {
$col = null;
}
$row[] = $col;
}
if ($this->cook($row)) {
yield $row;
$this->idest++;
}
$this->isrc++;
}
}
}

View File

@ -0,0 +1,97 @@
<?php
namespace nur\sery\ext\spreadsheet;
use nur\sery\file\csv\AbstractBuilder;
use nur\sery\file\csv\TAbstractBuilder;
use nur\sery\os\path;
use nur\sery\web\http;
use OpenSpout\Common\Entity\Style\Style;
use OpenSpout\Writer\Common\Creator\WriterEntityFactory;
use OpenSpout\Writer\WriterMultiSheetsAbstract;
use OpenSpout\Writer\XLSX\Entity\SheetView;
class SpoutBuilder extends AbstractBuilder {
use TAbstractBuilder;
/** @var string|int|null nom de la feuille dans laquelle écrire */
const WSNAME = null;
function __construct(?string $output, ?array $params=null) {
parent::__construct($output, $params);
switch (path::ext($this->output)) {
case ".ods":
$ss = WriterEntityFactory::createODSWriter();
break;
case ".xlsx":
default:
$ss = WriterEntityFactory::createXLSXWriter();
break;
}
$ss->writeToStream($this->getResource());
$this->ss = $ss;
$this->firstSheet = true;
$this->setWsname($params["wsname"] ?? static::WSNAME);
}
protected WriterMultiSheetsAbstract $ss;
const STYLE_ROW = 0, STYLE_HEADER = 1;
protected int $rowStyle;
protected bool $firstSheet;
/**
* @param string|int|null $wsname
*/
function setWsname($wsname): self {
$ss = $this->ss;
$this->rowStyle = self::STYLE_ROW;
if ($this->firstSheet) {
$this->firstSheet = false;
$ws = $ss->getCurrentSheet();
} else {
$ws = $ss->addNewSheetAndMakeItCurrent();
}
if ($wsname !== null) $ws->setName($wsname);
$ws->setSheetView((new SheetView())
->setFreezeRow(2)
);
return $this;
}
function _write(array $row): void {
$row = WriterEntityFactory::createRowFromArray($row);
if ($this->rowStyle === self::STYLE_HEADER) {
$row->setStyle((new Style())
->setFontBold()
);
}
$this->ss->addRow($row);
}
function writeHeaders(?array $headers=null): void {
$this->rowStyle = self::STYLE_HEADER;
parent::writeHeaders($headers);
$this->rowStyle = self::STYLE_ROW;
}
function _sendContentType(): void {
switch (path::ext($this->output)) {
case ".ods":
$contentType = "application/vnd.oasis.opendocument.spreadsheet";
break;
case ".xlsx":
default:
$contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
break;
}
http::content_type($contentType);
}
function _sendFile(): int {
$this->rewind();
$this->sendHeaders();
return $this->fpassthru();
}
}

View File

@ -0,0 +1,55 @@
<?php
namespace nur\sery\ext\spreadsheet;
use nur\sery\file\csv\AbstractReader;
use nur\sery\file\csv\TAbstractReader;
use OpenSpout\Reader\Common\Creator\ReaderEntityFactory;
class SpoutReader extends AbstractReader {
/** @var string|int|null nom de la feuille depuis laquelle lire */
const WSNAME = null;
function __construct($input, ?array $params=null) {
parent::__construct($input, $params);
$this->wsname = $params["wsname"] ?? static::WSNAME;
}
protected $wsname;
/**
* @param string|int|null $wsname
*/
function setWsname($wsname): self {
$this->wsname = $wsname;
return $this;
}
function getIterator() {
$ss = ReaderEntityFactory::createReaderFromFile($this->input);
try {
$found = false;
foreach ($ss->getSheetIterator() as $ws) {
if ($this->wsname === null || $this->wsname === $ws->getName()) {
$found = true;
break;
}
}
if (!$found) return;
$this->isrc = $this->idest = 0;
foreach ($ws->getRowIterator() as $row) {
$row = $row->toArray();
foreach ($row as &$col) {
$this->verifixCol($col);
}; unset($col);
if ($this->cook($row)) {
yield $row;
$this->idest++;
}
$this->isrc++;
}
} finally {
$ss->close();
}
}
}

View File

@ -1,118 +1,9 @@
<?php
namespace nur\sery\ext\spreadsheet;
use nur\sery\file\csv\AbstractBuilder;
use nur\sery\file\csv\TAbstractBuilder;
use nur\sery\os\path;
use nur\sery\web\http;
use PhpOffice\PhpSpreadsheet\Cell\IValueBinder;
use PhpOffice\PhpSpreadsheet\Cell\StringValueBinder;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PhpOffice\PhpSpreadsheet\Writer\Ods;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
/**
* Class SsBuilder: construction d'une feuille de calcul, pour envoi à
* l'utilisateur
*/
class SsBuilder extends AbstractBuilder {
use TAbstractBuilder;
/** @var string|int|null nom de la feuille dans laquelle écrire */
const WSNAME = null;
function __construct(?string $output, ?array $params=null) {
parent::__construct($output, $params);
$this->ss = new Spreadsheet();
$this->valueBinder = new StringValueBinder();
$this->setWsname($params["wsname"] ?? static::WSNAME);
}
protected Spreadsheet $ss;
protected IValueBinder $valueBinder;
protected ?Worksheet $ws;
protected int $nrow;
const STYLE_ROW = 0, STYLE_HEADER = 1;
protected int $rowStyle;
/**
* @param string|int|null $wsname
*/
function setWsname($wsname): self {
$ss = $this->ss;
$this->ws = null;
$this->nrow = 0;
$this->rowStyle = self::STYLE_ROW;
$ws = wsutils::get_ws($wsname, $ss);
if ($ws === null) {
$ws = $ss->createSheet()->setTitle($wsname);
$this->wroteHeaders = false;
} else {
$maxRow = wsutils::compute_max_coords($ws)[1];
$this->nrow = $maxRow - 1;
$this->wroteHeaders = $maxRow > 1;
}
$this->ws = $ws;
return $this;
}
function _write(array $row): void {
$ws = $this->ws;
$styleHeader = $this->rowStyle === self::STYLE_HEADER;
$nrow = ++$this->nrow;
$ncol = 1;
foreach ($row as $col) {
$ws->getCellByColumnAndRow($ncol++, $nrow)->setValue($col, $this->valueBinder);
}
if ($styleHeader) {
$ws->getStyle("$nrow:$nrow")->getFont()->setBold(true);
$maxcol = count($row);
for ($ncol = 1; $ncol <= $maxcol; $ncol++) {
$ws->getColumnDimensionByColumn($ncol)->setAutoSize(true);
}
}
}
function writeHeaders(?array $headers=null): void {
$this->rowStyle = self::STYLE_HEADER;
parent::writeHeaders($headers);
$this->rowStyle = self::STYLE_ROW;
}
function _sendContentType(): void {
switch (path::ext($this->output)) {
case ".ods":
$contentType = "application/vnd.oasis.opendocument.spreadsheet";
break;
case ".xlsx":
default:
$contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
break;
}
http::content_type($contentType);
}
function _sendFile(): int {
switch (path::ext($this->output)) {
case ".ods":
$writer = new Ods($this->ss);
break;
case ".xlsx":
default:
$writer = new Xlsx($this->ss);
break;
}
$this->rewind();
$writer->save($this->getResource());
$this->rewind();
$this->sendHeaders();
return $this->fpassthru();
}
class SsBuilder extends SpoutBuilder {
}

View File

@ -1,119 +1,8 @@
<?php
namespace nur\sery\ext\spreadsheet;
use nur\sery\cl;
use nur\sery\file\csv\AbstractReader;
use nur\sery\file\csv\TAbstractReader;
use PhpOffice\PhpSpreadsheet\Cell\DataType;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\RichText\RichText;
class SsReader extends AbstractReader {
class SsReader extends SpoutReader {
use TAbstractReader;
const DATETIME_FORMAT = 'dd/mm/yyyy hh:mm:ss';
const DATE_FORMAT = 'dd/mm/yyyy';
const TIME_FORMAT = 'hh:mm:ss';
const FORMAT_MAPPINGS = [
'mm/dd hh' => self::DATETIME_FORMAT,
'dd/mm hh' => self::DATETIME_FORMAT,
'mm/dd hh:mm' => self::DATETIME_FORMAT,
'dd/mm hh:mm' => self::DATETIME_FORMAT,
'mm/dd hh:mm:ss' => self::DATETIME_FORMAT,
'dd/mm hh:mm:ss' => self::DATETIME_FORMAT,
'mm/dd/yyyy hh' => self::DATETIME_FORMAT,
'dd/mm/yyyy hh' => self::DATETIME_FORMAT,
'mm/dd/yyyy hh:mm' => self::DATETIME_FORMAT,
'dd/mm/yyyy hh:mm' => self::DATETIME_FORMAT,
'mm/dd/yyyy hh:mm:ss' => self::DATETIME_FORMAT,
'dd/mm/yyyy hh:mm:ss' => self::DATETIME_FORMAT,
'yyyy/mm/dd hh' => self::DATETIME_FORMAT,
'yyyy/mm/dd hh:mm' => self::DATETIME_FORMAT,
'yyyy/mm/dd hh:mm:ss' => self::DATETIME_FORMAT,
'mm/dd' => self::DATE_FORMAT,
'dd/mm' => self::DATE_FORMAT,
'mm/dd/yyyy' => self::DATE_FORMAT,
'dd/mm/yyyy' => self::DATE_FORMAT,
'yyyy/mm/dd' => self::DATE_FORMAT,
'mm/yyyy' => self::DATE_FORMAT,
'hh AM/PM' => self::TIME_FORMAT,
'hh:mm AM/PM' => self::TIME_FORMAT,
'hh:mm:ss AM/PM' => self::TIME_FORMAT,
'hh' => self::TIME_FORMAT,
'hh:mm' => self::TIME_FORMAT,
'hh:mm:ss' => self::TIME_FORMAT,
'[hh]:mm:ss' => self::TIME_FORMAT,
'mm:ss' => self::TIME_FORMAT,
];
/** @var string|int|null nom de la feuille depuis laquelle lire */
const WSNAME = null;
function __construct($input, ?array $params=null) {
parent::__construct($input, $params);
$this->wsname = $params["wsname"] ?? static::WSNAME;
}
protected $wsname;
/**
* @param string|int|null $wsname
*/
function setWsname($wsname): self {
$this->wsname = $wsname;
return $this;
}
function getIterator() {
$ss = IOFactory::load($this->input);
$ws = wsutils::get_ws($this->wsname, $ss);
[$nbCols, $nbRows] = wsutils::compute_max_coords($ws);
$this->isrc = $this->idest = 0;
for ($nrow = 1; $nrow <= $nbRows; $nrow++) {
$row = [];
for ($ncol = 1; $ncol <= $nbCols; $ncol++) {
if ($ws->cellExistsByColumnAndRow($ncol, $nrow)) {
$cell = $ws->getCellByColumnAndRow($ncol, $nrow);
$col = $cell->getValue();
if ($col instanceof RichText) {
$col = $col->getPlainText();
} else {
$dataType = $cell->getDataType();
if ($dataType == DataType::TYPE_NUMERIC || $dataType == DataType::TYPE_FORMULA) {
# si c'est un format date, le forcer à une valeur standard
$origFormatCode = $cell->getStyle()->getNumberFormat()->getFormatCode();
if (strpbrk($origFormatCode, "ymdhs") !== false) {
$formatCode = $origFormatCode;
$formatCode = preg_replace('/y+/', "yyyy", $formatCode);
$formatCode = preg_replace('/m+/', "mm", $formatCode);
$formatCode = preg_replace('/d+/', "dd", $formatCode);
$formatCode = preg_replace('/h+/', "hh", $formatCode);
$formatCode = preg_replace('/s+/', "ss", $formatCode);
$formatCode = preg_replace('/-+/', "/", $formatCode);
$formatCode = preg_replace('/\\\\ /', " ", $formatCode);
$formatCode = preg_replace('/;@$/', "", $formatCode);
$formatCode = cl::get(self::FORMAT_MAPPINGS, $formatCode, $formatCode);
if ($formatCode !== $origFormatCode) {
$cell->getStyle()->getNumberFormat()->setFormatCode($formatCode);
}
}
}
$col = $cell->getFormattedValue();
$this->verifixCol($col);
}
} else {
$col = null;
}
$row[] = $col;
}
if ($this->cook($row)) {
yield $row;
$this->idest++;
}
$this->isrc++;
}
}
}