ajout SsReader et CsvReader

This commit is contained in:
Jephté Clain 2024-06-11 17:33:13 +04:00
parent 291b478d93
commit 6bffe89904
6 changed files with 237 additions and 1 deletions

View File

@ -1,10 +1,31 @@
<?php <?php
namespace nur\sery\ext\spreadsheet; namespace nur\sery\ext\spreadsheet;
use nur\sery\ValueException;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate; use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
class wsutils { class wsutils {
static function get_ws(?string $wsname, Spreadsheet $ss, bool $create=false): ?Worksheet {
if ($wsname == null) {
$ws = $ss->getActiveSheet();
} elseif (is_numeric($wsname)) {
$sheetCount = $ss->getSheetCount();
if ($wsname < 1 || $wsname > $sheetCount) {
throw ValueException::invalid_value($wsname, "sheet index");
}
$ws = $ss->getSheet($wsname - 1);
} else {
$ws = $ss->getSheetByName($wsname);
if ($ws === null) {
if ($create) $ws = $ss->createSheet()->setTitle($wsname);
else throw ValueException::invalid_value($wsname, "sheet name");
}
}
return $ws;
}
static function get_highest_coords(Worksheet $ws): array { static function get_highest_coords(Worksheet $ws): array {
$highestColumnA = $ws->getHighestColumn(); $highestColumnA = $ws->getHighestColumn();
$highestCol = Coordinate::columnIndexFromString($highestColumnA); $highestCol = Coordinate::columnIndexFromString($highestColumnA);

View File

@ -0,0 +1,45 @@
<?php
namespace nur\sery\file\csv;
use IteratorAggregate;
abstract class AbstractReader implements IReader {
const SCHEMA = null;
const HEADERS = null;
/** @var ?string nom du fichier depuis lequel lire */
const INPUT = null;
function __construct($input, ?array $params=null) {
if ($input !== null) $params["input"] = $input;
#
$this->schema = $params["schema"] ?? static::SCHEMA;
$this->headers = $params["headers"] ?? static::HEADERS;
$this->input = $params["input"] ?? static::INPUT;
}
protected ?array $schema;
protected ?array $headers;
protected $input;
protected int $isrc = 0, $idest = 0;
protected function cook(array &$row): bool {
if ($this->isrc == 0) {
# ligne d'en-tête
$headers = $this->headers;
if ($headers === null) {
if ($this->schema === null) $headers = null;
else $headers = array_keys($this->schema);
if ($headers === null) $headers = $row;
$this->headers = $headers;
}
return false;
}
$row = array_combine($this->headers, $row);
return true;
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace nur\sery\file\csv;
use nur\sery\file\FileReader;
use nur\sery\php\time\Date;
use nur\sery\php\time\DateTime;
class CsvReader extends AbstractReader {
function getIterator() {
$reader = new FileReader($this->input);
while (($row = $reader->fgetcsv()) !== null) {
foreach ($row as &$col) {
# conversion Date et DateTime
if (DateTime::isa_datetime($col, true)) {
$col = new DateTime($col);
} elseif (DateTime::isa_date($col, true)) {
$col = new Date($col);
}
}; unset($col);
if ($this->cook($row)) {
yield $row;
$this->idest++;
}
$this->isrc++;
}
$reader->close();
}
}

7
src/file/csv/IReader.php Normal file
View File

@ -0,0 +1,7 @@
<?php
namespace nur\sery\file\csv;
use IteratorAggregate;
interface IReader extends IteratorAggregate {
}

View File

@ -3,6 +3,7 @@ namespace nur\sery\file\csv;
use nur\sery\ext\spreadsheet\wsutils; use nur\sery\ext\spreadsheet\wsutils;
use nur\sery\os\path; use nur\sery\os\path;
use nur\sery\ValueException;
use nur\sery\web\http; use nur\sery\web\http;
use PhpOffice\PhpSpreadsheet\Cell\IValueBinder; use PhpOffice\PhpSpreadsheet\Cell\IValueBinder;
use PhpOffice\PhpSpreadsheet\Cell\StringValueBinder; use PhpOffice\PhpSpreadsheet\Cell\StringValueBinder;
@ -19,11 +20,14 @@ use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
class SsBuilder extends AbstractBuilder { class SsBuilder extends AbstractBuilder {
use TAbstractBuilder; use TAbstractBuilder;
/** @var ?string nom de la feuille dans laquelle écrire */
const WSNAME = null;
function __construct(?string $output, ?array $params=null) { function __construct(?string $output, ?array $params=null) {
parent::__construct($output, $params); parent::__construct($output, $params);
$this->ss = new Spreadsheet(); $this->ss = new Spreadsheet();
$this->valueBinder = new StringValueBinder(); $this->valueBinder = new StringValueBinder();
$this->setWsname($params["wsname"] ?? null); $this->setWsname($params["wsname"] ?? static::WSNAME);
} }
protected Spreadsheet $ss; protected Spreadsheet $ss;
@ -45,6 +49,12 @@ class SsBuilder extends AbstractBuilder {
$this->rowStyle = self::STYLE_ROW; $this->rowStyle = self::STYLE_ROW;
if ($wsname === null) { if ($wsname === null) {
$ws = $ss->getActiveSheet(); $ws = $ss->getActiveSheet();
} elseif (is_numeric($wsname)) {
$sheetCount = $ss->getSheetCount();
if ($wsname < 1 || $wsname > $sheetCount) {
throw ValueException::invalid_value($wsname, "sheet index");
}
$ws = $ss->getSheet($wsname - 1);
} else { } else {
$ws = $ss->getSheetByName($wsname); $ws = $ss->getSheetByName($wsname);
} }

124
src/file/csv/SsReader.php Normal file
View File

@ -0,0 +1,124 @@
<?php
namespace nur\sery\file\csv;
use nur\sery\cl;
use nur\sery\ext\spreadsheet\wsutils;
use nur\sery\file\web\Upload;
use nur\sery\php\time\Date;
use nur\sery\php\time\DateTime;
use PhpOffice\PhpSpreadsheet\Cell\DataType;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\RichText\RichText;
class SsReader extends AbstractReader {
static function with(Upload $upload): IReader {
if ($upload->isExt(".csv")) {
return new CsvReader($upload->tmpName);
} else {
return new static($upload->tmpName);
}
}
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 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 ?string $wsname;
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);
$value = $cell->getValue();
if ($value instanceof RichText) {
$value = $value->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);
}
}
}
$value = $cell->getFormattedValue();
# conversion Date et DateTime
if (DateTime::isa_datetime($value, true)) {
$value = new DateTime($value);
} elseif (DateTime::isa_date($value, true)) {
$value = new Date($value);
}
}
} else {
$value = null;
}
$row[] = $value;
}
if ($this->cook($row)) {
yield $row;
$this->idest++;
}
$this->isrc++;
}
}
}