diff --git a/src/ext/spreadsheet/wsutils.php b/src/ext/spreadsheet/wsutils.php index 0f83442..cc7107a 100644 --- a/src/ext/spreadsheet/wsutils.php +++ b/src/ext/spreadsheet/wsutils.php @@ -1,10 +1,31 @@ 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 { $highestColumnA = $ws->getHighestColumn(); $highestCol = Coordinate::columnIndexFromString($highestColumnA); diff --git a/src/file/csv/AbstractReader.php b/src/file/csv/AbstractReader.php new file mode 100644 index 0000000..e5b2a15 --- /dev/null +++ b/src/file/csv/AbstractReader.php @@ -0,0 +1,45 @@ +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; + } +} diff --git a/src/file/csv/CsvReader.php b/src/file/csv/CsvReader.php new file mode 100644 index 0000000..f83c507 --- /dev/null +++ b/src/file/csv/CsvReader.php @@ -0,0 +1,29 @@ +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(); + } +} diff --git a/src/file/csv/IReader.php b/src/file/csv/IReader.php new file mode 100644 index 0000000..c944aad --- /dev/null +++ b/src/file/csv/IReader.php @@ -0,0 +1,7 @@ +ss = new Spreadsheet(); $this->valueBinder = new StringValueBinder(); - $this->setWsname($params["wsname"] ?? null); + $this->setWsname($params["wsname"] ?? static::WSNAME); } protected Spreadsheet $ss; @@ -45,6 +49,12 @@ class SsBuilder extends AbstractBuilder { $this->rowStyle = self::STYLE_ROW; 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); } diff --git a/src/file/csv/SsReader.php b/src/file/csv/SsReader.php new file mode 100644 index 0000000..3ce06e6 --- /dev/null +++ b/src/file/csv/SsReader.php @@ -0,0 +1,124 @@ +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++; + } + } +}