Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
008fad3b69 | |||
909cd849cc | |||
b721c16335 | |||
2f3865addc | |||
37ce992b3e | |||
09540b1767 | |||
86f34af6f6 | |||
53188fd2d8 |
@ -4,5 +4,5 @@
|
||||
RUNPHP=
|
||||
|
||||
# Si RUNPHP n'est pas défini, les variables suivantes peuvent être définies
|
||||
DIST=d11
|
||||
DIST=d12
|
||||
#REGISTRY=pubdocker.univ-reunion.fr
|
||||
|
@ -19,28 +19,32 @@
|
||||
}
|
||||
},
|
||||
"replace": {
|
||||
"openspout/openspout": "v3.7.4"
|
||||
"openspout/openspout": "v4.27.0"
|
||||
},
|
||||
"require": {
|
||||
"nulib/php": "^7.4-dev",
|
||||
"nulib/php": "^8.2-dev",
|
||||
"ext-dom": "*",
|
||||
"ext-fileinfo": "*",
|
||||
"ext-filter": "*",
|
||||
"ext-libxml": "*",
|
||||
"ext-xmlreader": "*",
|
||||
"ext-zip": "*",
|
||||
"php": "^7.4"
|
||||
"php": "^8.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"nulib/tests": "^7.4",
|
||||
"friendsofphp/php-cs-fixer": "^3.4",
|
||||
"phpstan/phpstan": "^1.4",
|
||||
"phpstan/phpstan-phpunit": "^1.0",
|
||||
"nulib/tests": "^8.2",
|
||||
"friendsofphp/php-cs-fixer": "^3.64.0",
|
||||
"infection/infection": "^0.29.6",
|
||||
"phpbench/phpbench": "^1.3.1",
|
||||
"phpstan/phpstan": "^1.12.4",
|
||||
"phpstan/phpstan-phpunit": "^1.4.0",
|
||||
"phpstan/phpstan-strict-rules": "^1.6.1",
|
||||
"ext-zlib": "*"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"nulib\\": "src",
|
||||
"OpenSpout\\": "upstream-3.x/src"
|
||||
"OpenSpout\\": "upstream-4.x/src"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
|
91
patches/v4.27.0.patch
Normal file
91
patches/v4.27.0.patch
Normal file
@ -0,0 +1,91 @@
|
||||
diff --git a/src/Reader/XLSX/Helper/CellValueFormatter.php b/src/Reader/XLSX/Helper/CellValueFormatter.php
|
||||
index 776de0a..bc7a5c4 100644
|
||||
--- a/src/Reader/XLSX/Helper/CellValueFormatter.php
|
||||
+++ b/src/Reader/XLSX/Helper/CellValueFormatter.php
|
||||
@@ -284,9 +284,13 @@ final class CellValueFormatter
|
||||
\assert(false !== $dateObj);
|
||||
|
||||
if ($this->shouldFormatDates) {
|
||||
- $styleNumberFormatCode = $this->styleManager->getNumberFormatCode($cellStyleId);
|
||||
- $phpDateFormat = DateFormatHelper::toPHPDateFormat($styleNumberFormatCode);
|
||||
+ //$styleNumberFormatCode = $this->styleManager->getNumberFormatCode($cellStyleId);
|
||||
+ //$phpDateFormat = DateFormatHelper::toPHPDateFormat($styleNumberFormatCode);
|
||||
+ // Toujours utiliser le format français complet
|
||||
+ $phpDateFormat = "d/m/Y H:i:s";
|
||||
$cellValue = $dateObj->format($phpDateFormat);
|
||||
+ // Enlever la composante heure si elle n'existe pas
|
||||
+ $cellValue = preg_replace('/ 00:00:00$/', "", $cellValue);
|
||||
} else {
|
||||
$cellValue = $dateObj;
|
||||
}
|
||||
diff --git a/src/Reader/XLSX/Options.php b/src/Reader/XLSX/Options.php
|
||||
index 636d2a4..e8609b5 100644
|
||||
--- a/src/Reader/XLSX/Options.php
|
||||
+++ b/src/Reader/XLSX/Options.php
|
||||
@@ -10,7 +10,7 @@ final class Options
|
||||
{
|
||||
use TempFolderOptionTrait;
|
||||
|
||||
- public bool $SHOULD_FORMAT_DATES = false;
|
||||
+ public bool $SHOULD_FORMAT_DATES = true;
|
||||
public bool $SHOULD_PRESERVE_EMPTY_ROWS = false;
|
||||
public bool $SHOULD_USE_1904_DATES = false;
|
||||
public bool $SHOULD_LOAD_MERGE_CELLS = false;
|
||||
diff --git a/src/Writer/AbstractWriter.php b/src/Writer/AbstractWriter.php
|
||||
index eef34fb..a22e90f 100644
|
||||
--- a/src/Writer/AbstractWriter.php
|
||||
+++ b/src/Writer/AbstractWriter.php
|
||||
@@ -13,6 +13,12 @@ abstract class AbstractWriter implements WriterInterface
|
||||
/** @var resource Pointer to the file/stream we will write to */
|
||||
protected $filePointer;
|
||||
|
||||
+ /**
|
||||
+ * @var bool faut-il garder ouvert le flux quand {@link self::close()} est
|
||||
+ * appelé?
|
||||
+ */
|
||||
+ protected bool $dontCloseFilePointer = false;
|
||||
+
|
||||
/** @var string document creator */
|
||||
protected string $creator = 'OpenSpout';
|
||||
|
||||
@@ -20,7 +26,7 @@ abstract class AbstractWriter implements WriterInterface
|
||||
protected static string $headerContentType;
|
||||
|
||||
/** @var string Path to the output file */
|
||||
- private string $outputFilePath;
|
||||
+ private ?string $outputFilePath;
|
||||
|
||||
/** @var bool Indicates whether the writer has been opened or not */
|
||||
private bool $isWriterOpened = false;
|
||||
@@ -51,6 +57,20 @@ abstract class AbstractWriter implements WriterInterface
|
||||
$this->isWriterOpened = true;
|
||||
}
|
||||
|
||||
+ final public function writeToStream($filePointer): void
|
||||
+ {
|
||||
+ if (!is_resource($filePointer)) {
|
||||
+ throw new IOException("filePointer is not a resource");
|
||||
+ }
|
||||
+ $this->outputFilePath = null;
|
||||
+
|
||||
+ $this->filePointer = $filePointer;
|
||||
+ $this->dontCloseFilePointer = true;
|
||||
+
|
||||
+ $this->openWriter();
|
||||
+ $this->isWriterOpened = true;
|
||||
+ }
|
||||
+
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
@@ -140,7 +160,9 @@ abstract class AbstractWriter implements WriterInterface
|
||||
|
||||
$this->closeWriter();
|
||||
|
||||
- fclose($this->filePointer);
|
||||
+ if (!$this->dontCloseFilePointer) {
|
||||
+ fclose($this->filePointer);
|
||||
+ }
|
||||
|
||||
$this->isWriterOpened = false;
|
||||
}
|
@ -14,14 +14,22 @@ use nulib\ref\ext\spout\ref_builder_ods;
|
||||
use nulib\ref\ext\spout\ref_builder_xlsx;
|
||||
use nulib\str;
|
||||
use nulib\web\http;
|
||||
use OpenSpout\Common\Entity\Cell;
|
||||
use OpenSpout\Common\Entity\Style\Border;
|
||||
use OpenSpout\Common\Entity\Style\BorderPart;
|
||||
use OpenSpout\Common\Entity\Cell\DateTimeCell;
|
||||
use OpenSpout\Common\Entity\Cell\EmptyCell;
|
||||
use OpenSpout\Common\Entity\Cell\NumericCell;
|
||||
use OpenSpout\Common\Entity\Cell\StringCell;
|
||||
use OpenSpout\Common\Entity\Row;
|
||||
use OpenSpout\Common\Entity\Style\Color;
|
||||
use OpenSpout\Common\Entity\Style\Style;
|
||||
use OpenSpout\Common\Helper\CellTypeHelper;
|
||||
use OpenSpout\Writer\Common\Creator\WriterEntityFactory;
|
||||
use OpenSpout\Writer\WriterMultiSheetsAbstract;
|
||||
use OpenSpout\Writer\AbstractWriterMultiSheets;
|
||||
use OpenSpout\Writer\Common\AbstractOptions;
|
||||
use OpenSpout\Writer\ODS\Options as ODSOptions;
|
||||
use OpenSpout\Writer\ODS\Writer as ODSWriter;
|
||||
use OpenSpout\Writer\XLSX\Entity\SheetView;
|
||||
use OpenSpout\Writer\XLSX\Options as XLSXOptions;
|
||||
use OpenSpout\Writer\XLSX\Writer as XLSXWriter;
|
||||
|
||||
class SpoutBuilder extends AbstractBuilder {
|
||||
use TAbstractBuilder;
|
||||
@ -42,16 +50,16 @@ class SpoutBuilder extends AbstractBuilder {
|
||||
return $object;
|
||||
}
|
||||
|
||||
protected static function add_border_part(?Border &$border, string $name, ?array $params): void {
|
||||
protected static function add_border_part(?array &$parts, string $name, ?array $params): void {
|
||||
if ($params === null) return;
|
||||
if ($border === null) $border = new Border();
|
||||
$part = new BorderPart($name);
|
||||
if (($color = $params["color"] ?? null) !== null) {
|
||||
$part->setColor(cl::get(ref_builder::COLORS, $color, $color));
|
||||
$color = cl::get(ref_builder::COLORS, $color, $color);
|
||||
} else {
|
||||
$color = Color::BLACK;
|
||||
}
|
||||
if (($width = $params["width"] ?? null) !== null) $part->setWidth($width);
|
||||
if (($style = $params["style"] ?? null) !== null) $part->setStyle($style);
|
||||
$border->addPart($part);
|
||||
if (($width = $params["width"] ?? null) === null) $width = Border::WIDTH_MEDIUM;
|
||||
if (($style = $params["style"] ?? null) === null) $style = Border::STYLE_SOLID;
|
||||
$parts[] = new BorderPart($name, $color, $width, $style);
|
||||
}
|
||||
|
||||
protected static function set_defaults(?array &$params, string $key, array $defaults): void {
|
||||
@ -82,8 +90,9 @@ class SpoutBuilder extends AbstractBuilder {
|
||||
$style->setBackgroundColor(cl::get(ref_builder::COLORS, $color, $color));
|
||||
}
|
||||
if (($align = $cell["align"] ?? null) !== null) $style->setCellAlignment($align);
|
||||
//if (($align = $cell["valign"] ?? null) !== null) $style->setCellVerticalAlignment($align);
|
||||
if (($align = $cell["valign"] ?? null) !== null) $style->setCellVerticalAlignment($align);
|
||||
if (($wrap = $cell["wrap"] ?? null) !== null) $style->setShouldWrapText($wrap);
|
||||
if (($rotation = $cell["rotation"] ?? null) !== null) $style->setTextRotation($rotation);
|
||||
if (($format = $cell["format"] ?? null) !== null) $style->setFormat($format);
|
||||
if (($border = $cell["border"] ?? null) !== null) {
|
||||
if (is_string($border)) {
|
||||
@ -114,18 +123,14 @@ class SpoutBuilder extends AbstractBuilder {
|
||||
if ($colorAll !== null) $part["color"] = $colorAll;
|
||||
}; unset($part);
|
||||
}
|
||||
|
||||
$top = $border["top"] ?? null;
|
||||
$right = $border["right"] ?? null;
|
||||
$bottom = $border["bottom"] ?? null;
|
||||
$left = $border["left"] ?? null;
|
||||
$border = null;
|
||||
self::add_border_part($border, "top", $top);
|
||||
self::add_border_part($border, "right", $right);
|
||||
self::add_border_part($border, "bottom", $bottom);
|
||||
self::add_border_part($border, "left", $left);
|
||||
if ($border !== null) $style->setBorder($border);
|
||||
$parts = null;
|
||||
self::add_border_part($parts, "top", $border["top"] ?? null);
|
||||
self::add_border_part($parts, "right", $border["right"] ?? null);
|
||||
self::add_border_part($parts, "bottom", $border["bottom"] ?? null);
|
||||
self::add_border_part($parts, "left", $border["left"] ?? null);
|
||||
if ($parts !== null) $style->setBorder(new Border(...$parts));
|
||||
}
|
||||
if (($autofit = $cell["autofit"] ?? null) !== null) $style->setShouldShrinkToFit($autofit);
|
||||
return $style;
|
||||
}
|
||||
|
||||
@ -173,26 +178,31 @@ class SpoutBuilder extends AbstractBuilder {
|
||||
case "ods":
|
||||
case self::SS_TYPE_ODS:
|
||||
$ssType = self::SS_TYPE_ODS;
|
||||
$ssWriter = WriterEntityFactory::createODSWriter();
|
||||
self::apply_params($ssWriter, $spoutParams, ref_builder_ods::PARAMS_SPOUT);
|
||||
$ssOptions = new ODSOptions();
|
||||
$ssWriterClass = ODSWriter::class;
|
||||
$refParams = ref_builder_ods::PARAMS_SPOUT;
|
||||
break;
|
||||
case "xlsx":
|
||||
case self::SS_TYPE_XLSX:
|
||||
default:
|
||||
$ssType = self::SS_TYPE_XLSX;
|
||||
$ssWriter = WriterEntityFactory::createXLSXWriter();
|
||||
self::apply_params($ssWriter, $spoutParams, ref_builder_xlsx::PARAMS_SPOUT);
|
||||
$ssOptions = new XLSXOptions();
|
||||
$ssWriterClass = XLSXWriter::class;
|
||||
$refParams = ref_builder_xlsx::PARAMS_SPOUT;
|
||||
break;
|
||||
}
|
||||
$defaultColumnWidth = $spoutParams["default_column_width"] ?? null;
|
||||
if ($defaultColumnWidth !== null) $ssWriter->setDefaultColumnWidth($defaultColumnWidth);
|
||||
if ($defaultColumnWidth !== null) $ssOptions->DEFAULT_COLUMN_WIDTH = $defaultColumnWidth;
|
||||
$defaultRowHeight = $spoutParams["default_row_height"] ?? null;
|
||||
if ($defaultRowHeight !== null) $ssWriter->setDefaultRowHeight($defaultRowHeight);
|
||||
if ($defaultRowHeight !== null) $ssOptions->DEFAULT_ROW_HEIGHT = $defaultRowHeight;
|
||||
$defaultRowStyle = $spoutParams["default_row_style"] ?? null;
|
||||
if ($defaultRowStyle !== null) $ssWriter->setDefaultRowStyle($defaultRowStyle);
|
||||
if ($defaultRowStyle !== null) $ssOptions->DEFAULT_ROW_STYLE = $defaultRowStyle;
|
||||
self::apply_params($ssOptions, $spoutParams, $refParams);
|
||||
$ssWriter = new $ssWriterClass($ssOptions);
|
||||
$ssWriter->writeToStream($this->getResource());
|
||||
|
||||
$this->ssType = $ssType;
|
||||
$this->ssOptions = $ssOptions;
|
||||
$this->ssWriter = $ssWriter;
|
||||
$this->spoutParams = $spoutParams;
|
||||
$this->typeNumeric = boolval($params["type_numeric"] ?? static::TYPE_NUMERIC);
|
||||
@ -213,7 +223,9 @@ class SpoutBuilder extends AbstractBuilder {
|
||||
/** @var int type de fichier généré */
|
||||
protected int $ssType;
|
||||
|
||||
protected WriterMultiSheetsAbstract $ssWriter;
|
||||
protected AbstractOptions $ssOptions;
|
||||
|
||||
protected AbstractWriterMultiSheets $ssWriter;
|
||||
|
||||
protected ?array $spoutParams;
|
||||
|
||||
@ -338,7 +350,8 @@ class SpoutBuilder extends AbstractBuilder {
|
||||
* Les lignes sont indexées sur 1
|
||||
*/
|
||||
function mergeCells(int $topLeftCol, int $topLeftRow, int $bottomRightCol, int $bottomRightRow): void {
|
||||
$this->ssWriter->mergeCells([$topLeftCol, $topLeftRow], [$bottomRightCol, $bottomRightRow]);
|
||||
$sheet = $this->ssWriter->getCurrentSheet();
|
||||
$this->ssOptions->mergeCells($topLeftCol, $topLeftRow, $bottomRightCol, $bottomRightRow, $sheet->getIndex());
|
||||
}
|
||||
|
||||
protected function isNumeric($value): bool {
|
||||
@ -356,10 +369,6 @@ class SpoutBuilder extends AbstractBuilder {
|
||||
$style ??= new Style();
|
||||
$style->setFormat(self::DATETIME_FORMAT);
|
||||
return true;
|
||||
} elseif (CellTypeHelper::isDateTimeOrDateInterval($value)) {
|
||||
$style ??= new Style();
|
||||
$style->setFormat(self::DATE_FORMAT);
|
||||
return true;
|
||||
}
|
||||
if (!is_string($value) || !$this->typeDate) return false;
|
||||
if (DateTime::isa_datetime($value, true)) {
|
||||
@ -401,16 +410,14 @@ class SpoutBuilder extends AbstractBuilder {
|
||||
$style = $colStyles[$key] ?? null;
|
||||
self::ensure_style($style);
|
||||
if ($col === null || $col === "") {
|
||||
$type = Cell::TYPE_EMPTY;
|
||||
$cell = new EmptyCell(null, $style);
|
||||
} elseif ($this->isNumeric($col)) {
|
||||
$type = Cell::TYPE_NUMERIC;
|
||||
$cell = new NumericCell($col, $style);
|
||||
} elseif ($this->isDate($col, $style)) {
|
||||
$type = Cell::TYPE_DATE;
|
||||
$cell = new DateTimeCell($col, $style);
|
||||
} else {
|
||||
$type = Cell::TYPE_STRING;
|
||||
$cell = new StringCell($col, $style);
|
||||
}
|
||||
$cell = WriterEntityFactory::createCell($col, $style);
|
||||
$cell->setType($type);
|
||||
$cells[] = $cell;
|
||||
}
|
||||
|
||||
@ -421,7 +428,7 @@ class SpoutBuilder extends AbstractBuilder {
|
||||
}
|
||||
$rowStyle ??= $oddStyle;
|
||||
self::ensure_style($rowStyle);
|
||||
$row = WriterEntityFactory::createRow($cells, $rowStyle);
|
||||
$row = new Row($cells, $rowStyle);
|
||||
self::apply_params($row, $rowParams, ref_builder::ROW_PARAMS);
|
||||
|
||||
$mergeCells = $rowParams["merge_cells"] ?? null;
|
||||
|
@ -82,6 +82,7 @@ class ref_builder {
|
||||
"align" => "string",
|
||||
"valign" => "string",
|
||||
"wrap" => "bool",
|
||||
"rotation" => "int",
|
||||
"format" => "string",
|
||||
"border" => [
|
||||
"top" => ["color" => "string", "width" => "string", "style" => "string"],
|
||||
@ -89,5 +90,6 @@ class ref_builder {
|
||||
"bottom" => ["color" => "string", "width" => "string", "style" => "string"],
|
||||
"left" => ["color" => "string", "width" => "string", "style" => "string"],
|
||||
],
|
||||
"autofit" => "bool",
|
||||
];
|
||||
}
|
||||
|
@ -1,16 +1,43 @@
|
||||
<?php
|
||||
namespace nulib\ref\ext\spout;
|
||||
|
||||
use OpenSpout\Writer\XLSX\Options\HeaderFooter;
|
||||
use OpenSpout\Writer\XLSX\Options\PageMargin;
|
||||
use OpenSpout\Writer\XLSX\Options\PageSetup;
|
||||
|
||||
class ref_builder_xlsx extends ref_builder {
|
||||
const PARAMS_SHEET = [
|
||||
"view" => self::PARAMS_SHEET_VIEW,
|
||||
# copie de parent::SHEET
|
||||
"->setName" => ["string"],
|
||||
"->setIsVisible" => ["bool"],
|
||||
"header_style" => self::STYLE,
|
||||
"odd_style" => self::STYLE,
|
||||
"even_style" => self::STYLE,
|
||||
const PARAMS_SPOUT = [
|
||||
...parent::PARAMS_SPOUT,
|
||||
"->setPageSetup" => [
|
||||
PageSetup::class,
|
||||
"page_orientation" => "string",
|
||||
"page_size" => "string",
|
||||
"fit_to_height" => "bool",
|
||||
"fit_to_width" => "bool",
|
||||
"page_order" => "string",
|
||||
],
|
||||
"->setPageMargin" => [
|
||||
PageMargin::class,
|
||||
"top" => "float",
|
||||
"right" => "float",
|
||||
"bottom" => "float",
|
||||
"left" => "float",
|
||||
"header" => "float",
|
||||
"footer" => "float",
|
||||
],
|
||||
"->setHeaderFooter" => [
|
||||
HeaderFooter::class,
|
||||
"odd_header" => "string",
|
||||
"odd_footer" => "string",
|
||||
"even_header" => "string",
|
||||
"even_footer" => "string",
|
||||
"different_odd_even" => "bool",
|
||||
],
|
||||
];
|
||||
|
||||
const PARAMS_SHEET = [
|
||||
...parent::PARAMS_SHEET,
|
||||
"view" => self::PARAMS_SHEET_VIEW,
|
||||
];
|
||||
|
||||
const PARAMS_SHEET_VIEW = [
|
||||
|
@ -1,54 +0,0 @@
|
||||
# OpenSpout
|
||||
|
||||
[](https://packagist.org/packages/openspout/openspout)
|
||||
[](https://github.com/openspout/openspout/actions/workflows/ci.yml)
|
||||
[](https://codecov.io/gh/openspout/openspout?branch=main)
|
||||
[](https://packagist.org/packages/openspout/openspout)
|
||||
|
||||
OpenSpout is a community driven fork of `box/spout`, a PHP library to read and write spreadsheet files (CSV, XLSX and ODS), in a fast and scalable way.
|
||||
Unlike other file readers or writers, it is capable of processing very large files, while keeping the memory usage really low (less than 3MB).
|
||||
|
||||
## Documentation
|
||||
|
||||
Documentation can be found at [https://openspout.readthedocs.io/en/latest/](https://openspout.readthedocs.io/en/latest/).
|
||||
|
||||
## Requirements
|
||||
|
||||
* PHP version 7.3 or higher
|
||||
* PHP extension `php_zip` enabled
|
||||
* PHP extension `php_xmlreader` enabled
|
||||
|
||||
## Upgrade from `box/spout`
|
||||
|
||||
1. Replace `box/spout` with `openspout/openspout` in your `composer.json`
|
||||
2. Replace `Box\Spout` with `OpenSpout` in your code
|
||||
|
||||
## Upgrade guide
|
||||
|
||||
Version 3 introduced new functionality but also some breaking changes. If you want to upgrade your Spout codebase from version 2 please consult the [Upgrade guide](UPGRADE-3.0.md).
|
||||
|
||||
## Running tests
|
||||
|
||||
The `main` branch includes unit, functional and performance tests.
|
||||
If you just want to check that everything is working as expected, executing the unit and functional tests is enough.
|
||||
|
||||
* `phpunit` - runs unit and functional tests
|
||||
* `phpunit --group perf-tests` - only runs the performance tests
|
||||
|
||||
For information, the performance tests take about 10 minutes to run (processing 1 million rows files is not a quick thing).
|
||||
|
||||
> Performance tests status: [](https://travis-ci.org/box/spout)
|
||||
|
||||
## Copyright and License
|
||||
|
||||
This is a fork of Box's Spout library: https://github.com/box/spout
|
||||
|
||||
Code until and directly descending from commit [`cc42c1d`](https://github.com/openspout/openspout/commit/cc42c1d29fc5d29f07caeace99bd29dbb6d7c2f8)
|
||||
is copyright of _Box, Inc._ and licensed under the Apache License, Version 2.0:
|
||||
|
||||
https://github.com/openspout/openspout/blob/cc42c1d29fc5d29f07caeace99bd29dbb6d7c2f8/LICENSE
|
||||
|
||||
Code created, edited and released after the commit mentioned above
|
||||
is copyright of _openspout_ Github organization and licensed under MIT License.
|
||||
|
||||
https://github.com/openspout/openspout/blob/main/LICENSE
|
@ -1,147 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Autoloader;
|
||||
|
||||
/**
|
||||
* @see https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader-examples.md#class-example
|
||||
*/
|
||||
class Psr4Autoloader
|
||||
{
|
||||
/**
|
||||
* An associative array where the key is a namespace prefix and the value
|
||||
* is an array of base directories for classes in that namespace.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $prefixes = [];
|
||||
|
||||
/**
|
||||
* Register loader with SPL autoloader stack.
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
spl_autoload_register([$this, 'loadClass']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a base directory for a namespace prefix.
|
||||
*
|
||||
* @param string $prefix the namespace prefix
|
||||
* @param string $baseDir a base directory for class files in the
|
||||
* namespace
|
||||
* @param bool $prepend if true, prepend the base directory to the stack
|
||||
* instead of appending it; this causes it to be searched first rather
|
||||
* than last
|
||||
*/
|
||||
public function addNamespace($prefix, $baseDir, $prepend = false)
|
||||
{
|
||||
// normalize namespace prefix
|
||||
$prefix = trim($prefix, '\\').'\\';
|
||||
|
||||
// normalize the base directory with a trailing separator
|
||||
$baseDir = rtrim($baseDir, \DIRECTORY_SEPARATOR).'/';
|
||||
|
||||
// initialize the namespace prefix array
|
||||
if (false === isset($this->prefixes[$prefix])) {
|
||||
$this->prefixes[$prefix] = [];
|
||||
}
|
||||
|
||||
// retain the base directory for the namespace prefix
|
||||
if ($prepend) {
|
||||
array_unshift($this->prefixes[$prefix], $baseDir);
|
||||
} else {
|
||||
$this->prefixes[$prefix][] = $baseDir;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the class file for a given class name.
|
||||
*
|
||||
* @param string $class the fully-qualified class name
|
||||
*
|
||||
* @return mixed the mapped file name on success, or boolean false on
|
||||
* failure
|
||||
*/
|
||||
public function loadClass($class)
|
||||
{
|
||||
// the current namespace prefix
|
||||
$prefix = $class;
|
||||
|
||||
// work backwards through the namespace names of the fully-qualified
|
||||
// class name to find a mapped file name
|
||||
while (($pos = strrpos($prefix, '\\')) !== false) {
|
||||
// retain the trailing namespace separator in the prefix
|
||||
$prefix = substr($class, 0, $pos + 1);
|
||||
|
||||
// the rest is the relative class name
|
||||
$relativeClass = substr($class, $pos + 1);
|
||||
|
||||
// try to load a mapped file for the prefix and relative class
|
||||
$mappedFile = $this->loadMappedFile($prefix, $relativeClass);
|
||||
if (false !== $mappedFile) {
|
||||
return $mappedFile;
|
||||
}
|
||||
|
||||
// remove the trailing namespace separator for the next iteration
|
||||
// of strrpos()
|
||||
$prefix = rtrim($prefix, '\\');
|
||||
}
|
||||
|
||||
// never found a mapped file
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the mapped file for a namespace prefix and relative class.
|
||||
*
|
||||
* @param string $prefix the namespace prefix
|
||||
* @param string $relativeClass the relative class name
|
||||
*
|
||||
* @return mixed boolean false if no mapped file can be loaded, or the
|
||||
* name of the mapped file that was loaded
|
||||
*/
|
||||
protected function loadMappedFile($prefix, $relativeClass)
|
||||
{
|
||||
// are there any base directories for this namespace prefix?
|
||||
if (false === isset($this->prefixes[$prefix])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// look through base directories for this namespace prefix
|
||||
foreach ($this->prefixes[$prefix] as $baseDir) {
|
||||
// replace the namespace prefix with the base directory,
|
||||
// replace namespace separators with directory separators
|
||||
// in the relative class name, append with .php
|
||||
$file = $baseDir
|
||||
.str_replace('\\', '/', $relativeClass)
|
||||
.'.php';
|
||||
|
||||
// if the mapped file exists, require it
|
||||
if ($this->requireFile($file)) {
|
||||
// yes, we're done
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// never found it
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* If a file exists, require it from the file system.
|
||||
*
|
||||
* @param string $file the file to require
|
||||
*
|
||||
* @return bool true if the file exists, false if not
|
||||
*/
|
||||
protected function requireFile($file)
|
||||
{
|
||||
if (file_exists($file)) {
|
||||
require $file;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Autoloader;
|
||||
|
||||
require_once 'Psr4Autoloader.php';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
* Full path to "src/Spout" which is what we want "OpenSpout" to map to
|
||||
*/
|
||||
$srcBaseDirectory = \dirname(__DIR__);
|
||||
|
||||
$loader = new Psr4Autoloader();
|
||||
$loader->register();
|
||||
$loader->addNamespace('OpenSpout', $srcBaseDirectory);
|
@ -1,48 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Common\Creator;
|
||||
|
||||
use OpenSpout\Common\Helper\EncodingHelper;
|
||||
use OpenSpout\Common\Helper\FileSystemHelper;
|
||||
use OpenSpout\Common\Helper\GlobalFunctionsHelper;
|
||||
use OpenSpout\Common\Helper\StringHelper;
|
||||
|
||||
/**
|
||||
* Factory to create helpers.
|
||||
*/
|
||||
class HelperFactory
|
||||
{
|
||||
/**
|
||||
* @return GlobalFunctionsHelper
|
||||
*/
|
||||
public function createGlobalFunctionsHelper()
|
||||
{
|
||||
return new GlobalFunctionsHelper();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $baseFolderPath The path of the base folder where all the I/O can occur
|
||||
*
|
||||
* @return FileSystemHelper
|
||||
*/
|
||||
public function createFileSystemHelper($baseFolderPath)
|
||||
{
|
||||
return new FileSystemHelper($baseFolderPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return EncodingHelper
|
||||
*/
|
||||
public function createEncodingHelper(GlobalFunctionsHelper $globalFunctionsHelper)
|
||||
{
|
||||
return new EncodingHelper($globalFunctionsHelper);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return StringHelper
|
||||
*/
|
||||
public function createStringHelper()
|
||||
{
|
||||
return new StringHelper();
|
||||
}
|
||||
}
|
@ -1,227 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Common\Entity;
|
||||
|
||||
use OpenSpout\Common\Entity\Style\Style;
|
||||
use OpenSpout\Common\Helper\CellTypeHelper;
|
||||
|
||||
class Cell
|
||||
{
|
||||
/**
|
||||
* Numeric cell type (whole numbers, fractional numbers, dates).
|
||||
*/
|
||||
public const TYPE_NUMERIC = 0;
|
||||
|
||||
/**
|
||||
* String (text) cell type.
|
||||
*/
|
||||
public const TYPE_STRING = 1;
|
||||
|
||||
/**
|
||||
* Formula cell type
|
||||
* Not used at the moment.
|
||||
*/
|
||||
public const TYPE_FORMULA = 2;
|
||||
|
||||
/**
|
||||
* Empty cell type.
|
||||
*/
|
||||
public const TYPE_EMPTY = 3;
|
||||
|
||||
/**
|
||||
* Boolean cell type.
|
||||
*/
|
||||
public const TYPE_BOOLEAN = 4;
|
||||
|
||||
/**
|
||||
* Date cell type.
|
||||
*/
|
||||
public const TYPE_DATE = 5;
|
||||
|
||||
/**
|
||||
* Error cell type.
|
||||
*/
|
||||
public const TYPE_ERROR = 6;
|
||||
|
||||
/**
|
||||
* The value of this cell.
|
||||
*
|
||||
* @var null|mixed
|
||||
*/
|
||||
protected $value;
|
||||
|
||||
/**
|
||||
* The cell type.
|
||||
*
|
||||
* @var null|int
|
||||
*/
|
||||
protected $type;
|
||||
|
||||
/**
|
||||
* The cell style.
|
||||
*
|
||||
* @var Style
|
||||
*/
|
||||
protected $style;
|
||||
|
||||
/**
|
||||
* @param null|mixed $value
|
||||
*/
|
||||
public function __construct($value, Style $style = null)
|
||||
{
|
||||
$this->setValue($value);
|
||||
$this->setStyle($style);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return (string) $this->getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null|mixed $value
|
||||
*/
|
||||
public function setValue($value)
|
||||
{
|
||||
$this->value = $value;
|
||||
$this->type = $this->detectType($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|mixed
|
||||
*/
|
||||
public function getValue()
|
||||
{
|
||||
return !$this->isError() ? $this->value : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getValueEvenIfError()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null|Style $style
|
||||
*/
|
||||
public function setStyle($style)
|
||||
{
|
||||
$this->style = $style ?: new Style();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Style
|
||||
*/
|
||||
public function getStyle()
|
||||
{
|
||||
return $this->style;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|int
|
||||
*/
|
||||
public function getType()
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $type
|
||||
*/
|
||||
public function setType($type)
|
||||
{
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isBoolean()
|
||||
{
|
||||
return self::TYPE_BOOLEAN === $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmpty()
|
||||
{
|
||||
return self::TYPE_EMPTY === $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isNumeric()
|
||||
{
|
||||
return self::TYPE_NUMERIC === $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isString()
|
||||
{
|
||||
return self::TYPE_STRING === $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isDate()
|
||||
{
|
||||
return self::TYPE_DATE === $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isFormula()
|
||||
{
|
||||
return self::TYPE_FORMULA === $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isError()
|
||||
{
|
||||
return self::TYPE_ERROR === $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current value type.
|
||||
*
|
||||
* @param null|mixed $value
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function detectType($value)
|
||||
{
|
||||
if (CellTypeHelper::isBoolean($value)) {
|
||||
return self::TYPE_BOOLEAN;
|
||||
}
|
||||
if (CellTypeHelper::isEmpty($value)) {
|
||||
return self::TYPE_EMPTY;
|
||||
}
|
||||
if (CellTypeHelper::isNumeric($value)) {
|
||||
return self::TYPE_NUMERIC;
|
||||
}
|
||||
if (CellTypeHelper::isDateTimeOrDateInterval($value)) {
|
||||
return self::TYPE_DATE;
|
||||
}
|
||||
if (CellTypeHelper::isFormula($value)) {
|
||||
return self::TYPE_FORMULA;
|
||||
}
|
||||
if (CellTypeHelper::isNonEmptyString($value)) {
|
||||
return self::TYPE_STRING;
|
||||
}
|
||||
|
||||
return self::TYPE_ERROR;
|
||||
}
|
||||
}
|
@ -1,166 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Common\Entity;
|
||||
|
||||
use OpenSpout\Common\Entity\Style\Style;
|
||||
|
||||
class Row
|
||||
{
|
||||
/**
|
||||
* The cells in this row.
|
||||
*
|
||||
* @var Cell[]
|
||||
*/
|
||||
protected $cells = [];
|
||||
|
||||
/**
|
||||
* The row style.
|
||||
*
|
||||
* @var Style
|
||||
*/
|
||||
protected $style;
|
||||
|
||||
/**
|
||||
* Row height (default is 15).
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $height = '15';
|
||||
|
||||
/**
|
||||
* Row constructor.
|
||||
*
|
||||
* @param Cell[] $cells
|
||||
* @param null|Style $style
|
||||
*/
|
||||
public function __construct(array $cells, $style)
|
||||
{
|
||||
$this
|
||||
->setCells($cells)
|
||||
->setStyle($style)
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Cell[] $cells
|
||||
*/
|
||||
public function getCells()
|
||||
{
|
||||
return $this->cells;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Cell[] $cells
|
||||
*
|
||||
* @return Row
|
||||
*/
|
||||
public function setCells(array $cells)
|
||||
{
|
||||
$this->cells = [];
|
||||
foreach ($cells as $cell) {
|
||||
$this->addCell($cell);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $cellIndex
|
||||
*
|
||||
* @return Row
|
||||
*/
|
||||
public function setCellAtIndex(Cell $cell, $cellIndex)
|
||||
{
|
||||
$this->cells[$cellIndex] = $cell;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $cellIndex
|
||||
*
|
||||
* @return null|Cell
|
||||
*/
|
||||
public function getCellAtIndex($cellIndex)
|
||||
{
|
||||
return $this->cells[$cellIndex] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Row
|
||||
*/
|
||||
public function addCell(Cell $cell)
|
||||
{
|
||||
$this->cells[] = $cell;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getNumCells()
|
||||
{
|
||||
// When using "setCellAtIndex", it's possible to
|
||||
// have "$this->cells" contain holes.
|
||||
if (empty($this->cells)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return max(array_keys($this->cells)) + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Style
|
||||
*/
|
||||
public function getStyle()
|
||||
{
|
||||
return $this->style;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null|Style $style
|
||||
*
|
||||
* @return Row
|
||||
*/
|
||||
public function setStyle($style)
|
||||
{
|
||||
$this->style = $style ?: new Style();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array The row values, as array
|
||||
*/
|
||||
public function toArray()
|
||||
{
|
||||
return array_map(function (Cell $cell) {
|
||||
return $cell->getValue();
|
||||
}, $this->cells);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set row height.
|
||||
*
|
||||
* @param string $height
|
||||
*
|
||||
* @return Row
|
||||
*/
|
||||
public function setHeight($height)
|
||||
{
|
||||
$this->height = $height;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns row height.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getHeight()
|
||||
{
|
||||
return $this->height;
|
||||
}
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Common\Entity\Style;
|
||||
|
||||
class Border
|
||||
{
|
||||
public const LEFT = 'left';
|
||||
public const RIGHT = 'right';
|
||||
public const TOP = 'top';
|
||||
public const BOTTOM = 'bottom';
|
||||
|
||||
public const STYLE_NONE = 'none';
|
||||
public const STYLE_SOLID = 'solid';
|
||||
public const STYLE_DASHED = 'dashed';
|
||||
public const STYLE_DOTTED = 'dotted';
|
||||
public const STYLE_DOUBLE = 'double';
|
||||
|
||||
public const WIDTH_THIN = 'thin';
|
||||
public const WIDTH_MEDIUM = 'medium';
|
||||
public const WIDTH_THICK = 'thick';
|
||||
|
||||
/** @var array A list of BorderPart objects for this border. */
|
||||
private $parts = [];
|
||||
|
||||
public function __construct(array $borderParts = [])
|
||||
{
|
||||
$this->setParts($borderParts);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name The name of the border part
|
||||
*
|
||||
* @return null|BorderPart
|
||||
*/
|
||||
public function getPart($name)
|
||||
{
|
||||
return $this->hasPart($name) ? $this->parts[$name] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name The name of the border part
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasPart($name)
|
||||
{
|
||||
return isset($this->parts[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getParts()
|
||||
{
|
||||
return $this->parts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set BorderParts.
|
||||
*
|
||||
* @param array $parts
|
||||
*/
|
||||
public function setParts($parts)
|
||||
{
|
||||
$this->parts = [];
|
||||
foreach ($parts as $part) {
|
||||
$this->addPart($part);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Border
|
||||
*/
|
||||
public function addPart(BorderPart $borderPart)
|
||||
{
|
||||
$this->parts[$borderPart->getName()] = $borderPart;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
@ -1,181 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Common\Entity\Style;
|
||||
|
||||
use OpenSpout\Writer\Exception\Border\InvalidNameException;
|
||||
use OpenSpout\Writer\Exception\Border\InvalidStyleException;
|
||||
use OpenSpout\Writer\Exception\Border\InvalidWidthException;
|
||||
|
||||
class BorderPart
|
||||
{
|
||||
/**
|
||||
* @var string the style of this border part
|
||||
*/
|
||||
protected $style;
|
||||
|
||||
/**
|
||||
* @var string the name of this border part
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* @var string the color of this border part
|
||||
*/
|
||||
protected $color;
|
||||
|
||||
/**
|
||||
* @var string the width of this border part
|
||||
*/
|
||||
protected $width;
|
||||
|
||||
/**
|
||||
* @var array allowed style constants for parts
|
||||
*/
|
||||
protected static $allowedStyles = [
|
||||
'none',
|
||||
'solid',
|
||||
'dashed',
|
||||
'dotted',
|
||||
'double',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array allowed names constants for border parts
|
||||
*/
|
||||
protected static $allowedNames = [
|
||||
'left',
|
||||
'right',
|
||||
'top',
|
||||
'bottom',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array allowed width constants for border parts
|
||||
*/
|
||||
protected static $allowedWidths = [
|
||||
'thin',
|
||||
'medium',
|
||||
'thick',
|
||||
];
|
||||
|
||||
/**
|
||||
* @param string $name @see BorderPart::$allowedNames
|
||||
* @param string $color A RGB color code
|
||||
* @param string $width @see BorderPart::$allowedWidths
|
||||
* @param string $style @see BorderPart::$allowedStyles
|
||||
*
|
||||
* @throws InvalidNameException
|
||||
* @throws InvalidStyleException
|
||||
* @throws InvalidWidthException
|
||||
*/
|
||||
public function __construct($name, $color = Color::BLACK, $width = Border::WIDTH_MEDIUM, $style = Border::STYLE_SOLID)
|
||||
{
|
||||
$this->setName($name);
|
||||
$this->setColor($color);
|
||||
$this->setWidth($width);
|
||||
$this->setStyle($style);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name The name of the border part @see BorderPart::$allowedNames
|
||||
*
|
||||
* @throws InvalidNameException
|
||||
*/
|
||||
public function setName($name)
|
||||
{
|
||||
if (!\in_array($name, self::$allowedNames, true)) {
|
||||
throw new InvalidNameException($name);
|
||||
}
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getStyle()
|
||||
{
|
||||
return $this->style;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $style The style of the border part @see BorderPart::$allowedStyles
|
||||
*
|
||||
* @throws InvalidStyleException
|
||||
*/
|
||||
public function setStyle($style)
|
||||
{
|
||||
if (!\in_array($style, self::$allowedStyles, true)) {
|
||||
throw new InvalidStyleException($style);
|
||||
}
|
||||
$this->style = $style;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getColor()
|
||||
{
|
||||
return $this->color;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $color The color of the border part @see Color::rgb()
|
||||
*/
|
||||
public function setColor($color)
|
||||
{
|
||||
$this->color = $color;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getWidth()
|
||||
{
|
||||
return $this->width;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $width The width of the border part @see BorderPart::$allowedWidths
|
||||
*
|
||||
* @throws InvalidWidthException
|
||||
*/
|
||||
public function setWidth($width)
|
||||
{
|
||||
if (!\in_array($width, self::$allowedWidths, true)) {
|
||||
throw new InvalidWidthException($width);
|
||||
}
|
||||
$this->width = $width;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function getAllowedStyles()
|
||||
{
|
||||
return self::$allowedStyles;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function getAllowedNames()
|
||||
{
|
||||
return self::$allowedNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function getAllowedWidths()
|
||||
{
|
||||
return self::$allowedWidths;
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Common\Exception;
|
||||
|
||||
class EncodingConversionException extends SpoutException
|
||||
{
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Common\Exception;
|
||||
|
||||
class IOException extends SpoutException
|
||||
{
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Common\Exception;
|
||||
|
||||
class InvalidArgumentException extends SpoutException
|
||||
{
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Common\Exception;
|
||||
|
||||
class InvalidColorException extends SpoutException
|
||||
{
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Common\Exception;
|
||||
|
||||
abstract class SpoutException extends \Exception
|
||||
{
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Common\Exception;
|
||||
|
||||
class UnsupportedTypeException extends SpoutException
|
||||
{
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Common\Helper;
|
||||
|
||||
/**
|
||||
* This class provides helper functions to determine the type of the cell value.
|
||||
*/
|
||||
class CellTypeHelper
|
||||
{
|
||||
/**
|
||||
* @param null|mixed $value
|
||||
*
|
||||
* @return bool Whether the given value is considered "empty"
|
||||
*/
|
||||
public static function isEmpty($value)
|
||||
{
|
||||
return null === $value || '' === $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return bool Whether the given value is a non empty string
|
||||
*/
|
||||
public static function isNonEmptyString($value)
|
||||
{
|
||||
return 'string' === \gettype($value) && '' !== $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given value is numeric.
|
||||
* A numeric value is from type "integer" or "double" ("float" is not returned by gettype).
|
||||
*
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return bool Whether the given value is numeric
|
||||
*/
|
||||
public static function isNumeric($value)
|
||||
{
|
||||
$valueType = \gettype($value);
|
||||
|
||||
return 'integer' === $valueType || 'double' === $valueType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given value is boolean.
|
||||
* "true"/"false" and 0/1 are not booleans.
|
||||
*
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return bool Whether the given value is boolean
|
||||
*/
|
||||
public static function isBoolean($value)
|
||||
{
|
||||
return 'boolean' === \gettype($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given value is a DateTime or DateInterval object.
|
||||
*
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return bool Whether the given value is a DateTime or DateInterval object
|
||||
*/
|
||||
public static function isDateTimeOrDateInterval($value)
|
||||
{
|
||||
return
|
||||
$value instanceof \DateTimeInterface
|
||||
|| $value instanceof \DateInterval
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isFormula($value)
|
||||
{
|
||||
return \is_string($value) && isset($value[0]) && '=' === $value[0];
|
||||
}
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Common\Helper\Escaper;
|
||||
|
||||
/**
|
||||
* Provides functions to escape and unescape data for CSV files.
|
||||
*/
|
||||
class CSV implements EscaperInterface
|
||||
{
|
||||
/**
|
||||
* Escapes the given string to make it compatible with CSV.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param string $string The string to escape
|
||||
*
|
||||
* @return string The escaped string
|
||||
*/
|
||||
public function escape($string)
|
||||
{
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unescapes the given string to make it compatible with CSV.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param string $string The string to unescape
|
||||
*
|
||||
* @return string The unescaped string
|
||||
*/
|
||||
public function unescape($string)
|
||||
{
|
||||
return $string;
|
||||
}
|
||||
}
|
@ -1,63 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Common\Helper\Escaper;
|
||||
|
||||
/**
|
||||
* Provides functions to escape and unescape data for ODS files.
|
||||
*/
|
||||
class ODS implements EscaperInterface
|
||||
{
|
||||
/**
|
||||
* Escapes the given string to make it compatible with XLSX.
|
||||
*
|
||||
* @param string $string The string to escape
|
||||
*
|
||||
* @return string The escaped string
|
||||
*/
|
||||
public function escape($string)
|
||||
{
|
||||
// @NOTE: Using ENT_QUOTES as XML entities ('<', '>', '&') as well as
|
||||
// single/double quotes (for XML attributes) need to be encoded.
|
||||
if (\defined('ENT_DISALLOWED')) {
|
||||
/**
|
||||
* 'ENT_DISALLOWED' ensures that invalid characters in the given document type are replaced.
|
||||
* Otherwise control characters like a vertical tab "\v" will make the XML document unreadable by the XML processor.
|
||||
*
|
||||
* @see https://github.com/box/spout/issues/329
|
||||
*/
|
||||
$replacedString = htmlspecialchars($string, ENT_QUOTES | ENT_DISALLOWED, 'UTF-8');
|
||||
} else {
|
||||
// We are on hhvm or any other engine that does not support ENT_DISALLOWED.
|
||||
$escapedString = htmlspecialchars($string, ENT_QUOTES, 'UTF-8');
|
||||
|
||||
// control characters values are from 0 to 1F (hex values) in the ASCII table
|
||||
// some characters should not be escaped though: "\t", "\r" and "\n".
|
||||
$regexPattern = '[\x00-\x08'.
|
||||
// skipping "\t" (0x9) and "\n" (0xA)
|
||||
'\x0B-\x0C'.
|
||||
// skipping "\r" (0xD)
|
||||
'\x0E-\x1F]';
|
||||
$replacedString = preg_replace("/{$regexPattern}/", '<27>', $escapedString);
|
||||
}
|
||||
|
||||
return $replacedString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unescapes the given string to make it compatible with XLSX.
|
||||
*
|
||||
* @param string $string The string to unescape
|
||||
*
|
||||
* @return string The unescaped string
|
||||
*/
|
||||
public function unescape($string)
|
||||
{
|
||||
// ==============
|
||||
// = WARNING =
|
||||
// ==============
|
||||
// It is assumed that the given string has already had its XML entities decoded.
|
||||
// This is true if the string is coming from a DOMNode (as DOMNode already decode XML entities on creation).
|
||||
// Therefore there is no need to call "htmlspecialchars_decode()".
|
||||
return $string;
|
||||
}
|
||||
}
|
@ -1,371 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Common\Helper;
|
||||
|
||||
/**
|
||||
* This class wraps global functions to facilitate testing.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class GlobalFunctionsHelper
|
||||
{
|
||||
/**
|
||||
* Wrapper around global function fopen().
|
||||
*
|
||||
* @see fopen()
|
||||
*
|
||||
* @param string $fileName
|
||||
* @param string $mode
|
||||
*
|
||||
* @return bool|resource
|
||||
*/
|
||||
public function fopen($fileName, $mode)
|
||||
{
|
||||
return fopen($fileName, $mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around global function fgets().
|
||||
*
|
||||
* @see fgets()
|
||||
*
|
||||
* @param resource $handle
|
||||
* @param null|int $length
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function fgets($handle, $length = null)
|
||||
{
|
||||
return fgets($handle, $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around global function fputs().
|
||||
*
|
||||
* @see fputs()
|
||||
*
|
||||
* @param resource $handle
|
||||
* @param string $string
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function fputs($handle, $string)
|
||||
{
|
||||
return fwrite($handle, $string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around global function fflush().
|
||||
*
|
||||
* @see fflush()
|
||||
*
|
||||
* @param resource $handle
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function fflush($handle)
|
||||
{
|
||||
return fflush($handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around global function fseek().
|
||||
*
|
||||
* @see fseek()
|
||||
*
|
||||
* @param resource $handle
|
||||
* @param int $offset
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function fseek($handle, $offset)
|
||||
{
|
||||
return fseek($handle, $offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around global function fgetcsv().
|
||||
*
|
||||
* @see fgetcsv()
|
||||
*
|
||||
* @param resource $handle
|
||||
* @param null|int $length
|
||||
* @param null|string $delimiter
|
||||
* @param null|string $enclosure
|
||||
*
|
||||
* @return array|false
|
||||
*/
|
||||
public function fgetcsv($handle, $length = null, $delimiter = null, $enclosure = null)
|
||||
{
|
||||
/**
|
||||
* PHP uses '\' as the default escape character. This is not RFC-4180 compliant...
|
||||
* To fix that, simply disable the escape character.
|
||||
*
|
||||
* @see https://bugs.php.net/bug.php?id=43225
|
||||
* @see http://tools.ietf.org/html/rfc4180
|
||||
*/
|
||||
$escapeCharacter = \PHP_VERSION_ID >= 70400 ? '' : "\0";
|
||||
|
||||
return fgetcsv($handle, $length, $delimiter, $enclosure, $escapeCharacter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around global function fputcsv().
|
||||
*
|
||||
* @see fputcsv()
|
||||
*
|
||||
* @param resource $handle
|
||||
* @param null|string $delimiter
|
||||
* @param null|string $enclosure
|
||||
*
|
||||
* @return false|int
|
||||
*/
|
||||
public function fputcsv($handle, array $fields, $delimiter = null, $enclosure = null)
|
||||
{
|
||||
/**
|
||||
* PHP uses '\' as the default escape character. This is not RFC-4180 compliant...
|
||||
* To fix that, simply disable the escape character.
|
||||
*
|
||||
* @see https://bugs.php.net/bug.php?id=43225
|
||||
* @see http://tools.ietf.org/html/rfc4180
|
||||
*/
|
||||
$escapeCharacter = \PHP_VERSION_ID >= 70400 ? '' : "\0";
|
||||
|
||||
return fputcsv($handle, $fields, $delimiter, $enclosure, $escapeCharacter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around global function fwrite().
|
||||
*
|
||||
* @see fwrite()
|
||||
*
|
||||
* @param resource $handle
|
||||
* @param string $string
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function fwrite($handle, $string)
|
||||
{
|
||||
return fwrite($handle, $string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around global function fclose().
|
||||
*
|
||||
* @see fclose()
|
||||
*
|
||||
* @param resource $handle
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function fclose($handle)
|
||||
{
|
||||
return fclose($handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around global function rewind().
|
||||
*
|
||||
* @see rewind()
|
||||
*
|
||||
* @param resource $handle
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function rewind($handle)
|
||||
{
|
||||
return rewind($handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around global function file_exists().
|
||||
*
|
||||
* @see file_exists()
|
||||
*
|
||||
* @param string $fileName
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function file_exists($fileName)
|
||||
{
|
||||
return file_exists($fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around global function file_get_contents().
|
||||
*
|
||||
* @see file_get_contents()
|
||||
*
|
||||
* @param string $filePath
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function file_get_contents($filePath)
|
||||
{
|
||||
$realFilePath = $this->convertToUseRealPath($filePath);
|
||||
|
||||
return file_get_contents($realFilePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around global function feof().
|
||||
*
|
||||
* @see feof()
|
||||
*
|
||||
* @param resource $handle
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function feof($handle)
|
||||
{
|
||||
return feof($handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around global function is_readable().
|
||||
*
|
||||
* @see is_readable()
|
||||
*
|
||||
* @param string $fileName
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_readable($fileName)
|
||||
{
|
||||
return is_readable($fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around global function basename().
|
||||
*
|
||||
* @see basename()
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $suffix
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function basename($path, $suffix = '')
|
||||
{
|
||||
return basename($path, $suffix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around global function header().
|
||||
*
|
||||
* @see header()
|
||||
*
|
||||
* @param string $string
|
||||
*/
|
||||
public function header($string)
|
||||
{
|
||||
header($string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around global function ob_end_clean().
|
||||
*
|
||||
* @see ob_end_clean()
|
||||
*/
|
||||
public function ob_end_clean()
|
||||
{
|
||||
if (ob_get_length() > 0) {
|
||||
ob_end_clean();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around global function iconv().
|
||||
*
|
||||
* @see iconv()
|
||||
*
|
||||
* @param string $string The string to be converted
|
||||
* @param string $sourceEncoding The encoding of the source string
|
||||
* @param string $targetEncoding The encoding the source string should be converted to
|
||||
*
|
||||
* @return bool|string the converted string or FALSE on failure
|
||||
*/
|
||||
public function iconv($string, $sourceEncoding, $targetEncoding)
|
||||
{
|
||||
return iconv($sourceEncoding, $targetEncoding, $string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around global function mb_convert_encoding().
|
||||
*
|
||||
* @see mb_convert_encoding()
|
||||
*
|
||||
* @param string $string The string to be converted
|
||||
* @param string $sourceEncoding The encoding of the source string
|
||||
* @param string $targetEncoding The encoding the source string should be converted to
|
||||
*
|
||||
* @return bool|string the converted string or FALSE on failure
|
||||
*/
|
||||
public function mb_convert_encoding($string, $sourceEncoding, $targetEncoding)
|
||||
{
|
||||
return mb_convert_encoding($string, $targetEncoding, $sourceEncoding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around global function stream_get_wrappers().
|
||||
*
|
||||
* @see stream_get_wrappers()
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function stream_get_wrappers()
|
||||
{
|
||||
return stream_get_wrappers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around global function function_exists().
|
||||
*
|
||||
* @see function_exists()
|
||||
*
|
||||
* @param string $functionName
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function function_exists($functionName)
|
||||
{
|
||||
return \function_exists($functionName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the given file path to use a real path.
|
||||
* This is to avoid issues on some Windows setup.
|
||||
*
|
||||
* @param string $filePath File path
|
||||
*
|
||||
* @return string The file path using a real path
|
||||
*/
|
||||
protected function convertToUseRealPath($filePath)
|
||||
{
|
||||
$realFilePath = $filePath;
|
||||
|
||||
if ($this->isZipStream($filePath)) {
|
||||
if (preg_match('/zip:\/\/(.*)#(.*)/', $filePath, $matches)) {
|
||||
$documentPath = $matches[1];
|
||||
$documentInsideZipPath = $matches[2];
|
||||
$realFilePath = 'zip://'.realpath($documentPath).'#'.$documentInsideZipPath;
|
||||
}
|
||||
} else {
|
||||
$realFilePath = realpath($filePath);
|
||||
}
|
||||
|
||||
return $realFilePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given path is a zip stream.
|
||||
*
|
||||
* @param string $path Path pointing to a document
|
||||
*
|
||||
* @return bool TRUE if path is a zip stream, FALSE otherwise
|
||||
*/
|
||||
protected function isZipStream($path)
|
||||
{
|
||||
return 0 === strpos($path, 'zip://');
|
||||
}
|
||||
}
|
@ -1,108 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Common\Helper;
|
||||
|
||||
/**
|
||||
* This class provides helper functions to work with strings and multibyte strings.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class StringHelper
|
||||
{
|
||||
/** @var bool Whether the mbstring extension is loaded */
|
||||
protected $hasMbstringSupport;
|
||||
|
||||
/** @var bool Whether the code is running with PHP7 or older versions */
|
||||
private $isRunningPhp7OrOlder;
|
||||
|
||||
/** @var array Locale info, used for number formatting */
|
||||
private $localeInfo;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->hasMbstringSupport = \extension_loaded('mbstring');
|
||||
$this->isRunningPhp7OrOlder = version_compare(PHP_VERSION, '8.0.0') < 0;
|
||||
$this->localeInfo = localeconv();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the length of the given string.
|
||||
* It uses the multi-bytes function is available.
|
||||
*
|
||||
* @see strlen
|
||||
* @see mb_strlen
|
||||
*
|
||||
* @param string $string
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getStringLength($string)
|
||||
{
|
||||
return $this->hasMbstringSupport ? mb_strlen($string) : \strlen($string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the position of the first occurrence of the given character/substring within the given string.
|
||||
* It uses the multi-bytes function is available.
|
||||
*
|
||||
* @see strpos
|
||||
* @see mb_strpos
|
||||
*
|
||||
* @param string $char Needle
|
||||
* @param string $string Haystack
|
||||
*
|
||||
* @return int Char/substring's first occurrence position within the string if found (starts at 0) or -1 if not found
|
||||
*/
|
||||
public function getCharFirstOccurrencePosition($char, $string)
|
||||
{
|
||||
$position = $this->hasMbstringSupport ? mb_strpos($string, $char) : strpos($string, $char);
|
||||
|
||||
return (false !== $position) ? $position : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the position of the last occurrence of the given character/substring within the given string.
|
||||
* It uses the multi-bytes function is available.
|
||||
*
|
||||
* @see strrpos
|
||||
* @see mb_strrpos
|
||||
*
|
||||
* @param string $char Needle
|
||||
* @param string $string Haystack
|
||||
*
|
||||
* @return int Char/substring's last occurrence position within the string if found (starts at 0) or -1 if not found
|
||||
*/
|
||||
public function getCharLastOccurrencePosition($char, $string)
|
||||
{
|
||||
$position = $this->hasMbstringSupport ? mb_strrpos($string, $char) : strrpos($string, $char);
|
||||
|
||||
return (false !== $position) ? $position : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a numeric value (int or float) in a way that's compatible with the expected spreadsheet format.
|
||||
*
|
||||
* Formatting of float values is locale dependent in PHP < 8.
|
||||
* Thousands separators and decimal points vary from locale to locale (en_US: 12.34 vs pl_PL: 12,34).
|
||||
* However, float values must be formatted with no thousands separator and a "." as decimal point
|
||||
* to work properly. This method can be used to convert the value to the correct format before storing it.
|
||||
*
|
||||
* @see https://wiki.php.net/rfc/locale_independent_float_to_string for the changed behavior in PHP8.
|
||||
*
|
||||
* @param float|int $numericValue
|
||||
*
|
||||
* @return float|int|string
|
||||
*/
|
||||
public function formatNumericValue($numericValue)
|
||||
{
|
||||
if ($this->isRunningPhp7OrOlder && \is_float($numericValue)) {
|
||||
return str_replace(
|
||||
[$this->localeInfo['thousands_sep'], $this->localeInfo['decimal_point']],
|
||||
['', '.'],
|
||||
(string) $numericValue
|
||||
);
|
||||
}
|
||||
|
||||
return $numericValue;
|
||||
}
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Common\Manager;
|
||||
|
||||
abstract class OptionsManagerAbstract implements OptionsManagerInterface
|
||||
{
|
||||
public const PREFIX_OPTION = 'OPTION_';
|
||||
|
||||
/** @var string[] List of all supported option names */
|
||||
private $supportedOptions = [];
|
||||
|
||||
/** @var array Associative array [OPTION_NAME => OPTION_VALUE] */
|
||||
private $options = [];
|
||||
|
||||
/**
|
||||
* OptionsManagerAbstract constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->supportedOptions = $this->getSupportedOptions();
|
||||
$this->setDefaultOptions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the given option, if this option is supported.
|
||||
*
|
||||
* @param string $optionName
|
||||
* @param mixed $optionValue
|
||||
*/
|
||||
public function setOption($optionName, $optionValue)
|
||||
{
|
||||
if (\in_array($optionName, $this->supportedOptions, true)) {
|
||||
$this->options[$optionName] = $optionValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an option to the internal list of options
|
||||
* Used only for mergeCells() for now.
|
||||
*
|
||||
* @param mixed $optionName
|
||||
* @param mixed $optionValue
|
||||
*/
|
||||
public function addOption($optionName, $optionValue)
|
||||
{
|
||||
if (\in_array($optionName, $this->supportedOptions, true)) {
|
||||
if (!isset($this->options[$optionName])) {
|
||||
$this->options[$optionName] = [];
|
||||
} elseif (!\is_array($this->options[$optionName])) {
|
||||
$this->options[$optionName] = [$this->options[$optionName]];
|
||||
}
|
||||
$this->options[$optionName][] = $optionValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $optionName
|
||||
*
|
||||
* @return null|mixed The set option or NULL if no option with given name found
|
||||
*/
|
||||
public function getOption($optionName)
|
||||
{
|
||||
$optionValue = null;
|
||||
|
||||
if (isset($this->options[$optionName])) {
|
||||
$optionValue = $this->options[$optionName];
|
||||
}
|
||||
|
||||
return $optionValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array List of supported options
|
||||
*/
|
||||
abstract protected function getSupportedOptions();
|
||||
|
||||
/**
|
||||
* Sets the default options.
|
||||
* To be overriden by child classes.
|
||||
*/
|
||||
abstract protected function setDefaultOptions();
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Common\Manager;
|
||||
|
||||
/**
|
||||
* Interface OptionsManagerInterface.
|
||||
*/
|
||||
interface OptionsManagerInterface
|
||||
{
|
||||
/**
|
||||
* @param string $optionName
|
||||
* @param mixed $optionValue
|
||||
*/
|
||||
public function setOption($optionName, $optionValue);
|
||||
|
||||
/**
|
||||
* @param string $optionName
|
||||
*
|
||||
* @return null|mixed The set option or NULL if no option with given name found
|
||||
*/
|
||||
public function getOption($optionName);
|
||||
|
||||
/**
|
||||
* Add an option to the internal list of options
|
||||
* Used only for mergeCells() for now.
|
||||
*
|
||||
* @param mixed $optionName
|
||||
* @param mixed $optionValue
|
||||
*/
|
||||
public function addOption($optionName, $optionValue);
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Common;
|
||||
|
||||
/**
|
||||
* This class references the supported types.
|
||||
*/
|
||||
abstract class Type
|
||||
{
|
||||
public const CSV = 'csv';
|
||||
public const XLSX = 'xlsx';
|
||||
public const ODS = 'ods';
|
||||
}
|
@ -1,98 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Reader\CSV\Creator;
|
||||
|
||||
use OpenSpout\Common\Creator\HelperFactory;
|
||||
use OpenSpout\Common\Entity\Cell;
|
||||
use OpenSpout\Common\Entity\Row;
|
||||
use OpenSpout\Common\Helper\GlobalFunctionsHelper;
|
||||
use OpenSpout\Common\Manager\OptionsManagerInterface;
|
||||
use OpenSpout\Reader\Common\Creator\InternalEntityFactoryInterface;
|
||||
use OpenSpout\Reader\CSV\RowIterator;
|
||||
use OpenSpout\Reader\CSV\Sheet;
|
||||
use OpenSpout\Reader\CSV\SheetIterator;
|
||||
|
||||
/**
|
||||
* Factory to create entities.
|
||||
*/
|
||||
class InternalEntityFactory implements InternalEntityFactoryInterface
|
||||
{
|
||||
/** @var HelperFactory */
|
||||
private $helperFactory;
|
||||
|
||||
public function __construct(HelperFactory $helperFactory)
|
||||
{
|
||||
$this->helperFactory = $helperFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resource $filePointer Pointer to the CSV file to read
|
||||
* @param OptionsManagerInterface $optionsManager
|
||||
* @param GlobalFunctionsHelper $globalFunctionsHelper
|
||||
*
|
||||
* @return SheetIterator
|
||||
*/
|
||||
public function createSheetIterator($filePointer, $optionsManager, $globalFunctionsHelper)
|
||||
{
|
||||
$rowIterator = $this->createRowIterator($filePointer, $optionsManager, $globalFunctionsHelper);
|
||||
$sheet = $this->createSheet($rowIterator);
|
||||
|
||||
return new SheetIterator($sheet);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Cell[] $cells
|
||||
*
|
||||
* @return Row
|
||||
*/
|
||||
public function createRow(array $cells = [])
|
||||
{
|
||||
return new Row($cells, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $cellValue
|
||||
*
|
||||
* @return Cell
|
||||
*/
|
||||
public function createCell($cellValue)
|
||||
{
|
||||
return new Cell($cellValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Row
|
||||
*/
|
||||
public function createRowFromArray(array $cellValues = [])
|
||||
{
|
||||
$cells = array_map(function ($cellValue) {
|
||||
return $this->createCell($cellValue);
|
||||
}, $cellValues);
|
||||
|
||||
return $this->createRow($cells);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RowIterator $rowIterator
|
||||
*
|
||||
* @return Sheet
|
||||
*/
|
||||
private function createSheet($rowIterator)
|
||||
{
|
||||
return new Sheet($rowIterator);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resource $filePointer Pointer to the CSV file to read
|
||||
* @param OptionsManagerInterface $optionsManager
|
||||
* @param GlobalFunctionsHelper $globalFunctionsHelper
|
||||
*
|
||||
* @return RowIterator
|
||||
*/
|
||||
private function createRowIterator($filePointer, $optionsManager, $globalFunctionsHelper)
|
||||
{
|
||||
$encodingHelper = $this->helperFactory->createEncodingHelper($globalFunctionsHelper);
|
||||
|
||||
return new RowIterator($filePointer, $optionsManager, $encodingHelper, $this, $globalFunctionsHelper);
|
||||
}
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Reader\CSV\Manager;
|
||||
|
||||
use OpenSpout\Common\Helper\EncodingHelper;
|
||||
use OpenSpout\Common\Manager\OptionsManagerAbstract;
|
||||
use OpenSpout\Reader\Common\Entity\Options;
|
||||
|
||||
/**
|
||||
* CSV Reader options manager.
|
||||
*/
|
||||
class OptionsManager extends OptionsManagerAbstract
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getSupportedOptions()
|
||||
{
|
||||
return [
|
||||
Options::SHOULD_FORMAT_DATES,
|
||||
Options::SHOULD_PRESERVE_EMPTY_ROWS,
|
||||
Options::FIELD_DELIMITER,
|
||||
Options::FIELD_ENCLOSURE,
|
||||
Options::ENCODING,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setDefaultOptions()
|
||||
{
|
||||
$this->setOption(Options::SHOULD_FORMAT_DATES, false);
|
||||
$this->setOption(Options::SHOULD_PRESERVE_EMPTY_ROWS, false);
|
||||
$this->setOption(Options::FIELD_DELIMITER, ',');
|
||||
$this->setOption(Options::FIELD_ENCLOSURE, '"');
|
||||
$this->setOption(Options::ENCODING, EncodingHelper::ENCODING_UTF8);
|
||||
}
|
||||
}
|
@ -1,149 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Reader\CSV;
|
||||
|
||||
use OpenSpout\Common\Exception\IOException;
|
||||
use OpenSpout\Common\Helper\GlobalFunctionsHelper;
|
||||
use OpenSpout\Common\Manager\OptionsManagerInterface;
|
||||
use OpenSpout\Reader\Common\Creator\InternalEntityFactoryInterface;
|
||||
use OpenSpout\Reader\Common\Entity\Options;
|
||||
use OpenSpout\Reader\CSV\Creator\InternalEntityFactory;
|
||||
use OpenSpout\Reader\ReaderAbstract;
|
||||
|
||||
/**
|
||||
* This class provides support to read data from a CSV file.
|
||||
*/
|
||||
class Reader extends ReaderAbstract
|
||||
{
|
||||
/** @var resource Pointer to the file to be written */
|
||||
protected $filePointer;
|
||||
|
||||
/** @var SheetIterator To iterator over the CSV unique "sheet" */
|
||||
protected $sheetIterator;
|
||||
|
||||
/** @var string Original value for the "auto_detect_line_endings" INI value */
|
||||
protected $originalAutoDetectLineEndings;
|
||||
|
||||
/** @var bool Whether the code is running with PHP >= 8.1 */
|
||||
private $isRunningAtLeastPhp81;
|
||||
|
||||
public function __construct(
|
||||
OptionsManagerInterface $optionsManager,
|
||||
GlobalFunctionsHelper $globalFunctionsHelper,
|
||||
InternalEntityFactoryInterface $entityFactory
|
||||
) {
|
||||
parent::__construct($optionsManager, $globalFunctionsHelper, $entityFactory);
|
||||
$this->isRunningAtLeastPhp81 = version_compare(PHP_VERSION, '8.1.0') >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the field delimiter for the CSV.
|
||||
* Needs to be called before opening the reader.
|
||||
*
|
||||
* @param string $fieldDelimiter Character that delimits fields
|
||||
*
|
||||
* @return Reader
|
||||
*/
|
||||
public function setFieldDelimiter($fieldDelimiter)
|
||||
{
|
||||
$this->optionsManager->setOption(Options::FIELD_DELIMITER, $fieldDelimiter);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the field enclosure for the CSV.
|
||||
* Needs to be called before opening the reader.
|
||||
*
|
||||
* @param string $fieldEnclosure Character that enclose fields
|
||||
*
|
||||
* @return Reader
|
||||
*/
|
||||
public function setFieldEnclosure($fieldEnclosure)
|
||||
{
|
||||
$this->optionsManager->setOption(Options::FIELD_ENCLOSURE, $fieldEnclosure);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the encoding of the CSV file to be read.
|
||||
* Needs to be called before opening the reader.
|
||||
*
|
||||
* @param string $encoding Encoding of the CSV file to be read
|
||||
*
|
||||
* @return Reader
|
||||
*/
|
||||
public function setEncoding($encoding)
|
||||
{
|
||||
$this->optionsManager->setOption(Options::ENCODING, $encoding);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether stream wrappers are supported.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function doesSupportStreamWrapper()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the file at the given path to make it ready to be read.
|
||||
* If setEncoding() was not called, it assumes that the file is encoded in UTF-8.
|
||||
*
|
||||
* @param string $filePath Path of the CSV file to be read
|
||||
*
|
||||
* @throws \OpenSpout\Common\Exception\IOException
|
||||
*/
|
||||
protected function openReader($filePath)
|
||||
{
|
||||
// "auto_detect_line_endings" is deprecated in PHP 8.1
|
||||
if (!$this->isRunningAtLeastPhp81) {
|
||||
$this->originalAutoDetectLineEndings = ini_get('auto_detect_line_endings');
|
||||
ini_set('auto_detect_line_endings', '1');
|
||||
}
|
||||
|
||||
$this->filePointer = $this->globalFunctionsHelper->fopen($filePath, 'r');
|
||||
if (!$this->filePointer) {
|
||||
throw new IOException("Could not open file {$filePath} for reading.");
|
||||
}
|
||||
|
||||
/** @var InternalEntityFactory $entityFactory */
|
||||
$entityFactory = $this->entityFactory;
|
||||
|
||||
$this->sheetIterator = $entityFactory->createSheetIterator(
|
||||
$this->filePointer,
|
||||
$this->optionsManager,
|
||||
$this->globalFunctionsHelper
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator to iterate over sheets.
|
||||
*
|
||||
* @return SheetIterator To iterate over sheets
|
||||
*/
|
||||
protected function getConcreteSheetIterator()
|
||||
{
|
||||
return $this->sheetIterator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the reader. To be used after reading the file.
|
||||
*/
|
||||
protected function closeReader()
|
||||
{
|
||||
if (\is_resource($this->filePointer)) {
|
||||
$this->globalFunctionsHelper->fclose($this->filePointer);
|
||||
}
|
||||
|
||||
// "auto_detect_line_endings" is deprecated in PHP 8.1
|
||||
if (!$this->isRunningAtLeastPhp81) {
|
||||
ini_set('auto_detect_line_endings', $this->originalAutoDetectLineEndings);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Reader\Common\Creator;
|
||||
|
||||
use OpenSpout\Common\Entity\Cell;
|
||||
use OpenSpout\Common\Entity\Row;
|
||||
|
||||
/**
|
||||
* Interface EntityFactoryInterface.
|
||||
*/
|
||||
interface InternalEntityFactoryInterface
|
||||
{
|
||||
/**
|
||||
* @param Cell[] $cells
|
||||
*
|
||||
* @return Row
|
||||
*/
|
||||
public function createRow(array $cells = []);
|
||||
|
||||
/**
|
||||
* @param mixed $cellValue
|
||||
*
|
||||
* @return Cell
|
||||
*/
|
||||
public function createCell($cellValue);
|
||||
}
|
@ -1,72 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Reader\Common\Creator;
|
||||
|
||||
use OpenSpout\Common\Exception\UnsupportedTypeException;
|
||||
use OpenSpout\Common\Type;
|
||||
use OpenSpout\Reader\ReaderInterface;
|
||||
|
||||
/**
|
||||
* Factory to create external entities.
|
||||
*/
|
||||
class ReaderEntityFactory
|
||||
{
|
||||
/**
|
||||
* Creates a reader by file extension.
|
||||
*
|
||||
* @param string $path The path to the spreadsheet file. Supported extensions are .csv, .ods and .xlsx
|
||||
*
|
||||
* @throws \OpenSpout\Common\Exception\UnsupportedTypeException
|
||||
*
|
||||
* @return ReaderInterface
|
||||
*/
|
||||
public static function createReaderFromFile(string $path)
|
||||
{
|
||||
return ReaderFactory::createFromFile($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* This creates an instance of a CSV reader.
|
||||
*
|
||||
* @return \OpenSpout\Reader\CSV\Reader
|
||||
*/
|
||||
public static function createCSVReader()
|
||||
{
|
||||
try {
|
||||
return ReaderFactory::createFromType(Type::CSV);
|
||||
} catch (UnsupportedTypeException $e) {
|
||||
// should never happen
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This creates an instance of a XLSX reader.
|
||||
*
|
||||
* @return \OpenSpout\Reader\XLSX\Reader
|
||||
*/
|
||||
public static function createXLSXReader()
|
||||
{
|
||||
try {
|
||||
return ReaderFactory::createFromType(Type::XLSX);
|
||||
} catch (UnsupportedTypeException $e) {
|
||||
// should never happen
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This creates an instance of a ODS reader.
|
||||
*
|
||||
* @return \OpenSpout\Reader\ODS\Reader
|
||||
*/
|
||||
public static function createODSReader()
|
||||
{
|
||||
try {
|
||||
return ReaderFactory::createFromType(Type::ODS);
|
||||
} catch (UnsupportedTypeException $e) {
|
||||
// should never happen
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,109 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Reader\Common\Creator;
|
||||
|
||||
use OpenSpout\Common\Creator\HelperFactory;
|
||||
use OpenSpout\Common\Exception\UnsupportedTypeException;
|
||||
use OpenSpout\Common\Type;
|
||||
use OpenSpout\Reader\CSV\Creator\InternalEntityFactory as CSVInternalEntityFactory;
|
||||
use OpenSpout\Reader\CSV\Manager\OptionsManager as CSVOptionsManager;
|
||||
use OpenSpout\Reader\CSV\Reader as CSVReader;
|
||||
use OpenSpout\Reader\ODS\Creator\HelperFactory as ODSHelperFactory;
|
||||
use OpenSpout\Reader\ODS\Creator\InternalEntityFactory as ODSInternalEntityFactory;
|
||||
use OpenSpout\Reader\ODS\Creator\ManagerFactory as ODSManagerFactory;
|
||||
use OpenSpout\Reader\ODS\Manager\OptionsManager as ODSOptionsManager;
|
||||
use OpenSpout\Reader\ODS\Reader as ODSReader;
|
||||
use OpenSpout\Reader\ReaderInterface;
|
||||
use OpenSpout\Reader\XLSX\Creator\HelperFactory as XLSXHelperFactory;
|
||||
use OpenSpout\Reader\XLSX\Creator\InternalEntityFactory as XLSXInternalEntityFactory;
|
||||
use OpenSpout\Reader\XLSX\Creator\ManagerFactory as XLSXManagerFactory;
|
||||
use OpenSpout\Reader\XLSX\Manager\OptionsManager as XLSXOptionsManager;
|
||||
use OpenSpout\Reader\XLSX\Manager\SharedStringsCaching\CachingStrategyFactory;
|
||||
use OpenSpout\Reader\XLSX\Reader as XLSXReader;
|
||||
|
||||
/**
|
||||
* This factory is used to create readers, based on the type of the file to be read.
|
||||
* It supports CSV, XLSX and ODS formats.
|
||||
*/
|
||||
class ReaderFactory
|
||||
{
|
||||
/**
|
||||
* Creates a reader by file extension.
|
||||
*
|
||||
* @param string $path The path to the spreadsheet file. Supported extensions are .csv,.ods and .xlsx
|
||||
*
|
||||
* @throws \OpenSpout\Common\Exception\UnsupportedTypeException
|
||||
*
|
||||
* @return ReaderInterface
|
||||
*/
|
||||
public static function createFromFile(string $path)
|
||||
{
|
||||
$extension = strtolower(pathinfo($path, PATHINFO_EXTENSION));
|
||||
|
||||
return self::createFromType($extension);
|
||||
}
|
||||
|
||||
/**
|
||||
* This creates an instance of the appropriate reader, given the type of the file to be read.
|
||||
*
|
||||
* @param string $readerType Type of the reader to instantiate
|
||||
*
|
||||
* @throws \OpenSpout\Common\Exception\UnsupportedTypeException
|
||||
*
|
||||
* @return ReaderInterface
|
||||
*/
|
||||
public static function createFromType($readerType)
|
||||
{
|
||||
switch ($readerType) {
|
||||
case Type::CSV: return self::createCSVReader();
|
||||
|
||||
case Type::XLSX: return self::createXLSXReader();
|
||||
|
||||
case Type::ODS: return self::createODSReader();
|
||||
|
||||
default:
|
||||
throw new UnsupportedTypeException('No readers supporting the given type: '.$readerType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CSVReader
|
||||
*/
|
||||
private static function createCSVReader()
|
||||
{
|
||||
$optionsManager = new CSVOptionsManager();
|
||||
$helperFactory = new HelperFactory();
|
||||
$entityFactory = new CSVInternalEntityFactory($helperFactory);
|
||||
$globalFunctionsHelper = $helperFactory->createGlobalFunctionsHelper();
|
||||
|
||||
return new CSVReader($optionsManager, $globalFunctionsHelper, $entityFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return XLSXReader
|
||||
*/
|
||||
private static function createXLSXReader()
|
||||
{
|
||||
$optionsManager = new XLSXOptionsManager();
|
||||
$helperFactory = new XLSXHelperFactory();
|
||||
$managerFactory = new XLSXManagerFactory($helperFactory, new CachingStrategyFactory());
|
||||
$entityFactory = new XLSXInternalEntityFactory($managerFactory, $helperFactory);
|
||||
$globalFunctionsHelper = $helperFactory->createGlobalFunctionsHelper();
|
||||
|
||||
return new XLSXReader($optionsManager, $globalFunctionsHelper, $entityFactory, $managerFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ODSReader
|
||||
*/
|
||||
private static function createODSReader()
|
||||
{
|
||||
$optionsManager = new ODSOptionsManager();
|
||||
$helperFactory = new ODSHelperFactory();
|
||||
$managerFactory = new ODSManagerFactory();
|
||||
$entityFactory = new ODSInternalEntityFactory($helperFactory, $managerFactory);
|
||||
$globalFunctionsHelper = $helperFactory->createGlobalFunctionsHelper();
|
||||
|
||||
return new ODSReader($optionsManager, $globalFunctionsHelper, $entityFactory);
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Reader\Common\Entity;
|
||||
|
||||
/**
|
||||
* Readers' options holder.
|
||||
*/
|
||||
abstract class Options
|
||||
{
|
||||
// Common options
|
||||
public const SHOULD_FORMAT_DATES = 'shouldFormatDates';
|
||||
public const SHOULD_PRESERVE_EMPTY_ROWS = 'shouldPreserveEmptyRows';
|
||||
|
||||
// CSV specific options
|
||||
public const FIELD_DELIMITER = 'fieldDelimiter';
|
||||
public const FIELD_ENCLOSURE = 'fieldEnclosure';
|
||||
public const ENCODING = 'encoding';
|
||||
|
||||
// XLSX specific options
|
||||
public const TEMP_FOLDER = 'tempFolder';
|
||||
public const SHOULD_USE_1904_DATES = 'shouldUse1904Dates';
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Reader\Exception;
|
||||
|
||||
use Throwable;
|
||||
|
||||
class InvalidValueException extends ReaderException
|
||||
{
|
||||
/** @var mixed */
|
||||
private $invalidValue;
|
||||
|
||||
/**
|
||||
* @param mixed $invalidValue
|
||||
* @param string $message
|
||||
* @param int $code
|
||||
*/
|
||||
public function __construct($invalidValue, $message = '', $code = 0, Throwable $previous = null)
|
||||
{
|
||||
$this->invalidValue = $invalidValue;
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getInvalidValue()
|
||||
{
|
||||
return $this->invalidValue;
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Reader\Exception;
|
||||
|
||||
class IteratorNotRewindableException extends ReaderException
|
||||
{
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Reader\Exception;
|
||||
|
||||
class NoSheetsFoundException extends ReaderException
|
||||
{
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Reader\Exception;
|
||||
|
||||
use OpenSpout\Common\Exception\SpoutException;
|
||||
|
||||
abstract class ReaderException extends SpoutException
|
||||
{
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Reader\Exception;
|
||||
|
||||
class ReaderNotOpenedException extends ReaderException
|
||||
{
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Reader\Exception;
|
||||
|
||||
class SharedStringNotFoundException extends ReaderException
|
||||
{
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Reader\Exception;
|
||||
|
||||
class XMLProcessingException extends ReaderException
|
||||
{
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Reader;
|
||||
|
||||
/**
|
||||
* Interface IteratorInterface.
|
||||
*/
|
||||
interface IteratorInterface extends \Iterator
|
||||
{
|
||||
/**
|
||||
* Cleans up what was created to iterate over the object.
|
||||
*/
|
||||
public function end();
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Reader\ODS\Creator;
|
||||
|
||||
use OpenSpout\Reader\ODS\Helper\CellValueFormatter;
|
||||
use OpenSpout\Reader\ODS\Helper\SettingsHelper;
|
||||
|
||||
/**
|
||||
* Factory to create helpers.
|
||||
*/
|
||||
class HelperFactory extends \OpenSpout\Common\Creator\HelperFactory
|
||||
{
|
||||
/**
|
||||
* @param bool $shouldFormatDates Whether date/time values should be returned as PHP objects or be formatted as strings
|
||||
*
|
||||
* @return CellValueFormatter
|
||||
*/
|
||||
public function createCellValueFormatter($shouldFormatDates)
|
||||
{
|
||||
$escaper = $this->createStringsEscaper();
|
||||
|
||||
return new CellValueFormatter($shouldFormatDates, $escaper);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InternalEntityFactory $entityFactory
|
||||
*
|
||||
* @return SettingsHelper
|
||||
*/
|
||||
public function createSettingsHelper($entityFactory)
|
||||
{
|
||||
return new SettingsHelper($entityFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \OpenSpout\Common\Helper\Escaper\ODS
|
||||
*/
|
||||
public function createStringsEscaper()
|
||||
{
|
||||
// @noinspection PhpUnnecessaryFullyQualifiedNameInspection
|
||||
return new \OpenSpout\Common\Helper\Escaper\ODS();
|
||||
}
|
||||
}
|
@ -1,124 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Reader\ODS\Creator;
|
||||
|
||||
use OpenSpout\Common\Entity\Cell;
|
||||
use OpenSpout\Common\Entity\Row;
|
||||
use OpenSpout\Reader\Common\Creator\InternalEntityFactoryInterface;
|
||||
use OpenSpout\Reader\Common\Entity\Options;
|
||||
use OpenSpout\Reader\Common\XMLProcessor;
|
||||
use OpenSpout\Reader\ODS\RowIterator;
|
||||
use OpenSpout\Reader\ODS\Sheet;
|
||||
use OpenSpout\Reader\ODS\SheetIterator;
|
||||
use OpenSpout\Reader\Wrapper\XMLReader;
|
||||
|
||||
/**
|
||||
* Factory to create entities.
|
||||
*/
|
||||
class InternalEntityFactory implements InternalEntityFactoryInterface
|
||||
{
|
||||
/** @var HelperFactory */
|
||||
private $helperFactory;
|
||||
|
||||
/** @var ManagerFactory */
|
||||
private $managerFactory;
|
||||
|
||||
public function __construct(HelperFactory $helperFactory, ManagerFactory $managerFactory)
|
||||
{
|
||||
$this->helperFactory = $helperFactory;
|
||||
$this->managerFactory = $managerFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filePath Path of the file to be read
|
||||
* @param \OpenSpout\Common\Manager\OptionsManagerInterface $optionsManager Reader's options manager
|
||||
*
|
||||
* @return SheetIterator
|
||||
*/
|
||||
public function createSheetIterator($filePath, $optionsManager)
|
||||
{
|
||||
$escaper = $this->helperFactory->createStringsEscaper();
|
||||
$settingsHelper = $this->helperFactory->createSettingsHelper($this);
|
||||
|
||||
return new SheetIterator($filePath, $optionsManager, $escaper, $settingsHelper, $this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param XMLReader $xmlReader XML Reader
|
||||
* @param int $sheetIndex Index of the sheet, based on order in the workbook (zero-based)
|
||||
* @param string $sheetName Name of the sheet
|
||||
* @param bool $isSheetActive Whether the sheet was defined as active
|
||||
* @param bool $isSheetVisible Whether the sheet is visible
|
||||
* @param \OpenSpout\Common\Manager\OptionsManagerInterface $optionsManager Reader's options manager
|
||||
*
|
||||
* @return Sheet
|
||||
*/
|
||||
public function createSheet($xmlReader, $sheetIndex, $sheetName, $isSheetActive, $isSheetVisible, $optionsManager)
|
||||
{
|
||||
$rowIterator = $this->createRowIterator($xmlReader, $optionsManager);
|
||||
|
||||
return new Sheet($rowIterator, $sheetIndex, $sheetName, $isSheetActive, $isSheetVisible);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Cell[] $cells
|
||||
*
|
||||
* @return Row
|
||||
*/
|
||||
public function createRow(array $cells = [])
|
||||
{
|
||||
return new Row($cells, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $cellValue
|
||||
*
|
||||
* @return Cell
|
||||
*/
|
||||
public function createCell($cellValue)
|
||||
{
|
||||
return new Cell($cellValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return XMLReader
|
||||
*/
|
||||
public function createXMLReader()
|
||||
{
|
||||
return new XMLReader();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \ZipArchive
|
||||
*/
|
||||
public function createZipArchive()
|
||||
{
|
||||
return new \ZipArchive();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param XMLReader $xmlReader XML Reader
|
||||
* @param \OpenSpout\Common\Manager\OptionsManagerInterface $optionsManager Reader's options manager
|
||||
*
|
||||
* @return RowIterator
|
||||
*/
|
||||
private function createRowIterator($xmlReader, $optionsManager)
|
||||
{
|
||||
$shouldFormatDates = $optionsManager->getOption(Options::SHOULD_FORMAT_DATES);
|
||||
$cellValueFormatter = $this->helperFactory->createCellValueFormatter($shouldFormatDates);
|
||||
$xmlProcessor = $this->createXMLProcessor($xmlReader);
|
||||
$rowManager = $this->managerFactory->createRowManager($this);
|
||||
|
||||
return new RowIterator($xmlReader, $optionsManager, $cellValueFormatter, $xmlProcessor, $rowManager, $this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param XMLReader $xmlReader
|
||||
*
|
||||
* @return XMLProcessor
|
||||
*/
|
||||
private function createXMLProcessor($xmlReader)
|
||||
{
|
||||
return new XMLProcessor($xmlReader);
|
||||
}
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Reader\ODS\Creator;
|
||||
|
||||
use OpenSpout\Reader\Common\Manager\RowManager;
|
||||
|
||||
/**
|
||||
* Factory to create managers.
|
||||
*/
|
||||
class ManagerFactory
|
||||
{
|
||||
/**
|
||||
* @param InternalEntityFactory $entityFactory Factory to create entities
|
||||
*
|
||||
* @return RowManager
|
||||
*/
|
||||
public function createRowManager($entityFactory)
|
||||
{
|
||||
return new RowManager($entityFactory);
|
||||
}
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Reader\ODS\Manager;
|
||||
|
||||
use OpenSpout\Common\Manager\OptionsManagerAbstract;
|
||||
use OpenSpout\Reader\Common\Entity\Options;
|
||||
|
||||
/**
|
||||
* ODS Reader options manager.
|
||||
*/
|
||||
class OptionsManager extends OptionsManagerAbstract
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getSupportedOptions()
|
||||
{
|
||||
return [
|
||||
Options::SHOULD_FORMAT_DATES,
|
||||
Options::SHOULD_PRESERVE_EMPTY_ROWS,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setDefaultOptions()
|
||||
{
|
||||
$this->setOption(Options::SHOULD_FORMAT_DATES, false);
|
||||
$this->setOption(Options::SHOULD_PRESERVE_EMPTY_ROWS, false);
|
||||
}
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Reader\ODS;
|
||||
|
||||
use OpenSpout\Common\Exception\IOException;
|
||||
use OpenSpout\Reader\ODS\Creator\InternalEntityFactory;
|
||||
use OpenSpout\Reader\ReaderAbstract;
|
||||
|
||||
/**
|
||||
* This class provides support to read data from a ODS file.
|
||||
*/
|
||||
class Reader extends ReaderAbstract
|
||||
{
|
||||
/** @var \ZipArchive */
|
||||
protected $zip;
|
||||
|
||||
/** @var SheetIterator To iterator over the ODS sheets */
|
||||
protected $sheetIterator;
|
||||
|
||||
/**
|
||||
* Returns whether stream wrappers are supported.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function doesSupportStreamWrapper()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the file at the given file path to make it ready to be read.
|
||||
*
|
||||
* @param string $filePath Path of the file to be read
|
||||
*
|
||||
* @throws \OpenSpout\Common\Exception\IOException If the file at the given path or its content cannot be read
|
||||
* @throws \OpenSpout\Reader\Exception\NoSheetsFoundException If there are no sheets in the file
|
||||
*/
|
||||
protected function openReader($filePath)
|
||||
{
|
||||
/** @var InternalEntityFactory $entityFactory */
|
||||
$entityFactory = $this->entityFactory;
|
||||
|
||||
$this->zip = $entityFactory->createZipArchive();
|
||||
|
||||
if (true === $this->zip->open($filePath)) {
|
||||
/** @var InternalEntityFactory $entityFactory */
|
||||
$entityFactory = $this->entityFactory;
|
||||
$this->sheetIterator = $entityFactory->createSheetIterator($filePath, $this->optionsManager);
|
||||
} else {
|
||||
throw new IOException("Could not open {$filePath} for reading.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator to iterate over sheets.
|
||||
*
|
||||
* @return SheetIterator To iterate over sheets
|
||||
*/
|
||||
protected function getConcreteSheetIterator()
|
||||
{
|
||||
return $this->sheetIterator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the reader. To be used after reading the file.
|
||||
*/
|
||||
protected function closeReader()
|
||||
{
|
||||
if (null !== $this->zip) {
|
||||
$this->zip->close();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenSpout\Reader;
|
||||
|
||||
use OpenSpout\Common\Entity\Row;
|
||||
|
||||
interface RowIteratorInterface extends IteratorInterface
|
||||
{
|
||||
/**
|
||||
* Cleans up what was created to iterate over the object.
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function end();
|
||||
|
||||
/**
|
||||
* @return null|Row
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function current();
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Reader;
|
||||
|
||||
/**
|
||||
* Interface SheetInterface.
|
||||
*/
|
||||
interface SheetInterface
|
||||
{
|
||||
/**
|
||||
* @return IteratorInterface iterator to iterate over the sheet's rows
|
||||
*/
|
||||
public function getRowIterator();
|
||||
|
||||
/**
|
||||
* @return int Index of the sheet
|
||||
*/
|
||||
public function getIndex();
|
||||
|
||||
/**
|
||||
* @return string Name of the sheet
|
||||
*/
|
||||
public function getName();
|
||||
|
||||
/**
|
||||
* @return bool Whether the sheet was defined as active
|
||||
*/
|
||||
public function isActive();
|
||||
|
||||
/**
|
||||
* @return bool Whether the sheet is visible
|
||||
*/
|
||||
public function isVisible();
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenSpout\Reader;
|
||||
|
||||
/**
|
||||
* Interface IteratorInterface.
|
||||
*/
|
||||
interface SheetIteratorInterface extends IteratorInterface
|
||||
{
|
||||
/**
|
||||
* Cleans up what was created to iterate over the object.
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function end();
|
||||
|
||||
/**
|
||||
* @return null|SheetInterface
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function current();
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Reader\XLSX\Creator;
|
||||
|
||||
use OpenSpout\Common\Helper\Escaper;
|
||||
use OpenSpout\Reader\XLSX\Helper\CellValueFormatter;
|
||||
use OpenSpout\Reader\XLSX\Manager\SharedStringsManager;
|
||||
use OpenSpout\Reader\XLSX\Manager\StyleManager;
|
||||
|
||||
/**
|
||||
* Factory to create helpers.
|
||||
*/
|
||||
class HelperFactory extends \OpenSpout\Common\Creator\HelperFactory
|
||||
{
|
||||
/**
|
||||
* @param SharedStringsManager $sharedStringsManager Manages shared strings
|
||||
* @param StyleManager $styleManager Manages styles
|
||||
* @param bool $shouldFormatDates Whether date/time values should be returned as PHP objects or be formatted as strings
|
||||
* @param bool $shouldUse1904Dates Whether date/time values should use a calendar starting in 1904 instead of 1900
|
||||
*
|
||||
* @return CellValueFormatter
|
||||
*/
|
||||
public function createCellValueFormatter($sharedStringsManager, $styleManager, $shouldFormatDates, $shouldUse1904Dates)
|
||||
{
|
||||
$escaper = $this->createStringsEscaper();
|
||||
|
||||
return new CellValueFormatter($sharedStringsManager, $styleManager, $shouldFormatDates, $shouldUse1904Dates, $escaper);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Escaper\XLSX
|
||||
*/
|
||||
public function createStringsEscaper()
|
||||
{
|
||||
// @noinspection PhpUnnecessaryFullyQualifiedNameInspection
|
||||
return new Escaper\XLSX();
|
||||
}
|
||||
}
|
@ -1,163 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Reader\XLSX\Creator;
|
||||
|
||||
use OpenSpout\Common\Entity\Cell;
|
||||
use OpenSpout\Common\Entity\Row;
|
||||
use OpenSpout\Reader\Common\Creator\InternalEntityFactoryInterface;
|
||||
use OpenSpout\Reader\Common\Entity\Options;
|
||||
use OpenSpout\Reader\Common\XMLProcessor;
|
||||
use OpenSpout\Reader\Wrapper\XMLReader;
|
||||
use OpenSpout\Reader\XLSX\Manager\SharedStringsManager;
|
||||
use OpenSpout\Reader\XLSX\RowIterator;
|
||||
use OpenSpout\Reader\XLSX\Sheet;
|
||||
use OpenSpout\Reader\XLSX\SheetIterator;
|
||||
|
||||
/**
|
||||
* Factory to create entities.
|
||||
*/
|
||||
class InternalEntityFactory implements InternalEntityFactoryInterface
|
||||
{
|
||||
/** @var HelperFactory */
|
||||
private $helperFactory;
|
||||
|
||||
/** @var ManagerFactory */
|
||||
private $managerFactory;
|
||||
|
||||
public function __construct(ManagerFactory $managerFactory, HelperFactory $helperFactory)
|
||||
{
|
||||
$this->managerFactory = $managerFactory;
|
||||
$this->helperFactory = $helperFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filePath Path of the file to be read
|
||||
* @param \OpenSpout\Common\Manager\OptionsManagerInterface $optionsManager Reader's options manager
|
||||
* @param SharedStringsManager $sharedStringsManager Manages shared strings
|
||||
*
|
||||
* @return SheetIterator
|
||||
*/
|
||||
public function createSheetIterator($filePath, $optionsManager, $sharedStringsManager)
|
||||
{
|
||||
$sheetManager = $this->managerFactory->createSheetManager(
|
||||
$filePath,
|
||||
$optionsManager,
|
||||
$sharedStringsManager,
|
||||
$this
|
||||
);
|
||||
|
||||
return new SheetIterator($sheetManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filePath Path of the XLSX file being read
|
||||
* @param string $sheetDataXMLFilePath Path of the sheet data XML file as in [Content_Types].xml
|
||||
* @param int $sheetIndex Index of the sheet, based on order in the workbook (zero-based)
|
||||
* @param string $sheetName Name of the sheet
|
||||
* @param bool $isSheetActive Whether the sheet was defined as active
|
||||
* @param bool $isSheetVisible Whether the sheet is visible
|
||||
* @param \OpenSpout\Common\Manager\OptionsManagerInterface $optionsManager Reader's options manager
|
||||
* @param SharedStringsManager $sharedStringsManager Manages shared strings
|
||||
*
|
||||
* @return Sheet
|
||||
*/
|
||||
public function createSheet(
|
||||
$filePath,
|
||||
$sheetDataXMLFilePath,
|
||||
$sheetIndex,
|
||||
$sheetName,
|
||||
$isSheetActive,
|
||||
$isSheetVisible,
|
||||
$optionsManager,
|
||||
$sharedStringsManager
|
||||
) {
|
||||
$rowIterator = $this->createRowIterator($filePath, $sheetDataXMLFilePath, $optionsManager, $sharedStringsManager);
|
||||
|
||||
return new Sheet($rowIterator, $sheetIndex, $sheetName, $isSheetActive, $isSheetVisible);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Cell[] $cells
|
||||
*
|
||||
* @return Row
|
||||
*/
|
||||
public function createRow(array $cells = [])
|
||||
{
|
||||
return new Row($cells, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $cellValue
|
||||
*
|
||||
* @return Cell
|
||||
*/
|
||||
public function createCell($cellValue)
|
||||
{
|
||||
return new Cell($cellValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \ZipArchive
|
||||
*/
|
||||
public function createZipArchive()
|
||||
{
|
||||
return new \ZipArchive();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return XMLReader
|
||||
*/
|
||||
public function createXMLReader()
|
||||
{
|
||||
return new XMLReader();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param XMLReader $xmlReader
|
||||
*
|
||||
* @return XMLProcessor
|
||||
*/
|
||||
public function createXMLProcessor($xmlReader)
|
||||
{
|
||||
return new XMLProcessor($xmlReader);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filePath Path of the XLSX file being read
|
||||
* @param string $sheetDataXMLFilePath Path of the sheet data XML file as in [Content_Types].xml
|
||||
* @param \OpenSpout\Common\Manager\OptionsManagerInterface $optionsManager Reader's options manager
|
||||
* @param SharedStringsManager $sharedStringsManager Manages shared strings
|
||||
*
|
||||
* @return RowIterator
|
||||
*/
|
||||
private function createRowIterator($filePath, $sheetDataXMLFilePath, $optionsManager, $sharedStringsManager)
|
||||
{
|
||||
$xmlReader = $this->createXMLReader();
|
||||
$xmlProcessor = $this->createXMLProcessor($xmlReader);
|
||||
|
||||
$styleManager = $this->managerFactory->createStyleManager($filePath, $this);
|
||||
$rowManager = $this->managerFactory->createRowManager($this);
|
||||
$shouldFormatDates = $optionsManager->getOption(Options::SHOULD_FORMAT_DATES);
|
||||
$shouldUse1904Dates = $optionsManager->getOption(Options::SHOULD_USE_1904_DATES);
|
||||
|
||||
$cellValueFormatter = $this->helperFactory->createCellValueFormatter(
|
||||
$sharedStringsManager,
|
||||
$styleManager,
|
||||
$shouldFormatDates,
|
||||
$shouldUse1904Dates
|
||||
);
|
||||
|
||||
$shouldPreserveEmptyRows = $optionsManager->getOption(Options::SHOULD_PRESERVE_EMPTY_ROWS);
|
||||
|
||||
return new RowIterator(
|
||||
$filePath,
|
||||
$sheetDataXMLFilePath,
|
||||
$shouldPreserveEmptyRows,
|
||||
$xmlReader,
|
||||
$xmlProcessor,
|
||||
$cellValueFormatter,
|
||||
$rowManager,
|
||||
$this
|
||||
);
|
||||
}
|
||||
}
|
@ -1,109 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Reader\XLSX\Creator;
|
||||
|
||||
use OpenSpout\Reader\Common\Manager\RowManager;
|
||||
use OpenSpout\Reader\XLSX\Manager\SharedStringsCaching\CachingStrategyFactory;
|
||||
use OpenSpout\Reader\XLSX\Manager\SharedStringsManager;
|
||||
use OpenSpout\Reader\XLSX\Manager\SheetManager;
|
||||
use OpenSpout\Reader\XLSX\Manager\StyleManager;
|
||||
use OpenSpout\Reader\XLSX\Manager\WorkbookRelationshipsManager;
|
||||
|
||||
/**
|
||||
* Factory to create managers.
|
||||
*/
|
||||
class ManagerFactory
|
||||
{
|
||||
/** @var HelperFactory */
|
||||
private $helperFactory;
|
||||
|
||||
/** @var CachingStrategyFactory */
|
||||
private $cachingStrategyFactory;
|
||||
|
||||
/** @var null|WorkbookRelationshipsManager */
|
||||
private $cachedWorkbookRelationshipsManager;
|
||||
|
||||
/**
|
||||
* @param HelperFactory $helperFactory Factory to create helpers
|
||||
* @param CachingStrategyFactory $cachingStrategyFactory Factory to create shared strings caching strategies
|
||||
*/
|
||||
public function __construct(HelperFactory $helperFactory, CachingStrategyFactory $cachingStrategyFactory)
|
||||
{
|
||||
$this->helperFactory = $helperFactory;
|
||||
$this->cachingStrategyFactory = $cachingStrategyFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filePath Path of the XLSX file being read
|
||||
* @param string $tempFolder Temporary folder where the temporary files to store shared strings will be stored
|
||||
* @param InternalEntityFactory $entityFactory Factory to create entities
|
||||
*
|
||||
* @return SharedStringsManager
|
||||
*/
|
||||
public function createSharedStringsManager($filePath, $tempFolder, $entityFactory)
|
||||
{
|
||||
$workbookRelationshipsManager = $this->createWorkbookRelationshipsManager($filePath, $entityFactory);
|
||||
|
||||
return new SharedStringsManager(
|
||||
$filePath,
|
||||
$tempFolder,
|
||||
$workbookRelationshipsManager,
|
||||
$entityFactory,
|
||||
$this->helperFactory,
|
||||
$this->cachingStrategyFactory
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filePath Path of the XLSX file being read
|
||||
* @param \OpenSpout\Common\Manager\OptionsManagerInterface $optionsManager Reader's options manager
|
||||
* @param \OpenSpout\Reader\XLSX\Manager\SharedStringsManager $sharedStringsManager Manages shared strings
|
||||
* @param InternalEntityFactory $entityFactory Factory to create entities
|
||||
*
|
||||
* @return SheetManager
|
||||
*/
|
||||
public function createSheetManager($filePath, $optionsManager, $sharedStringsManager, $entityFactory)
|
||||
{
|
||||
$escaper = $this->helperFactory->createStringsEscaper();
|
||||
|
||||
return new SheetManager($filePath, $optionsManager, $sharedStringsManager, $escaper, $entityFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filePath Path of the XLSX file being read
|
||||
* @param InternalEntityFactory $entityFactory Factory to create entities
|
||||
*
|
||||
* @return StyleManager
|
||||
*/
|
||||
public function createStyleManager($filePath, $entityFactory)
|
||||
{
|
||||
$workbookRelationshipsManager = $this->createWorkbookRelationshipsManager($filePath, $entityFactory);
|
||||
|
||||
return new StyleManager($filePath, $workbookRelationshipsManager, $entityFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InternalEntityFactory $entityFactory Factory to create entities
|
||||
*
|
||||
* @return RowManager
|
||||
*/
|
||||
public function createRowManager($entityFactory)
|
||||
{
|
||||
return new RowManager($entityFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filePath Path of the XLSX file being read
|
||||
* @param InternalEntityFactory $entityFactory Factory to create entities
|
||||
*
|
||||
* @return WorkbookRelationshipsManager
|
||||
*/
|
||||
private function createWorkbookRelationshipsManager($filePath, $entityFactory)
|
||||
{
|
||||
if (!isset($this->cachedWorkbookRelationshipsManager)) {
|
||||
$this->cachedWorkbookRelationshipsManager = new WorkbookRelationshipsManager($filePath, $entityFactory);
|
||||
}
|
||||
|
||||
return $this->cachedWorkbookRelationshipsManager;
|
||||
}
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Reader\XLSX\Manager;
|
||||
|
||||
use OpenSpout\Common\Manager\OptionsManagerAbstract;
|
||||
use OpenSpout\Reader\Common\Entity\Options;
|
||||
|
||||
/**
|
||||
* XLSX Reader options manager.
|
||||
*/
|
||||
class OptionsManager extends OptionsManagerAbstract
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getSupportedOptions()
|
||||
{
|
||||
return [
|
||||
Options::TEMP_FOLDER,
|
||||
Options::SHOULD_FORMAT_DATES,
|
||||
Options::SHOULD_PRESERVE_EMPTY_ROWS,
|
||||
Options::SHOULD_USE_1904_DATES,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setDefaultOptions()
|
||||
{
|
||||
$this->setOption(Options::TEMP_FOLDER, sys_get_temp_dir());
|
||||
$this->setOption(Options::SHOULD_FORMAT_DATES, true);
|
||||
$this->setOption(Options::SHOULD_PRESERVE_EMPTY_ROWS, false);
|
||||
$this->setOption(Options::SHOULD_USE_1904_DATES, false);
|
||||
}
|
||||
}
|
@ -1,122 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Reader\XLSX;
|
||||
|
||||
use OpenSpout\Common\Exception\IOException;
|
||||
use OpenSpout\Common\Helper\GlobalFunctionsHelper;
|
||||
use OpenSpout\Common\Manager\OptionsManagerInterface;
|
||||
use OpenSpout\Reader\Common\Creator\InternalEntityFactoryInterface;
|
||||
use OpenSpout\Reader\Common\Entity\Options;
|
||||
use OpenSpout\Reader\ReaderAbstract;
|
||||
use OpenSpout\Reader\XLSX\Creator\InternalEntityFactory;
|
||||
use OpenSpout\Reader\XLSX\Creator\ManagerFactory;
|
||||
|
||||
/**
|
||||
* This class provides support to read data from a XLSX file.
|
||||
*/
|
||||
class Reader extends ReaderAbstract
|
||||
{
|
||||
/** @var ManagerFactory */
|
||||
protected $managerFactory;
|
||||
|
||||
/** @var \ZipArchive */
|
||||
protected $zip;
|
||||
|
||||
/** @var \OpenSpout\Reader\XLSX\Manager\SharedStringsManager Manages shared strings */
|
||||
protected $sharedStringsManager;
|
||||
|
||||
/** @var SheetIterator To iterator over the XLSX sheets */
|
||||
protected $sheetIterator;
|
||||
|
||||
public function __construct(
|
||||
OptionsManagerInterface $optionsManager,
|
||||
GlobalFunctionsHelper $globalFunctionsHelper,
|
||||
InternalEntityFactoryInterface $entityFactory,
|
||||
ManagerFactory $managerFactory
|
||||
) {
|
||||
parent::__construct($optionsManager, $globalFunctionsHelper, $entityFactory);
|
||||
$this->managerFactory = $managerFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $tempFolder Temporary folder where the temporary files will be created
|
||||
*
|
||||
* @return Reader
|
||||
*/
|
||||
public function setTempFolder($tempFolder)
|
||||
{
|
||||
$this->optionsManager->setOption(Options::TEMP_FOLDER, $tempFolder);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether stream wrappers are supported.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function doesSupportStreamWrapper()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the file at the given file path to make it ready to be read.
|
||||
* It also parses the sharedStrings.xml file to get all the shared strings available in memory
|
||||
* and fetches all the available sheets.
|
||||
*
|
||||
* @param string $filePath Path of the file to be read
|
||||
*
|
||||
* @throws \OpenSpout\Common\Exception\IOException If the file at the given path or its content cannot be read
|
||||
* @throws \OpenSpout\Reader\Exception\NoSheetsFoundException If there are no sheets in the file
|
||||
*/
|
||||
protected function openReader($filePath)
|
||||
{
|
||||
/** @var InternalEntityFactory $entityFactory */
|
||||
$entityFactory = $this->entityFactory;
|
||||
|
||||
$this->zip = $entityFactory->createZipArchive();
|
||||
|
||||
if (true === $this->zip->open($filePath)) {
|
||||
$tempFolder = $this->optionsManager->getOption(Options::TEMP_FOLDER);
|
||||
$this->sharedStringsManager = $this->managerFactory->createSharedStringsManager($filePath, $tempFolder, $entityFactory);
|
||||
|
||||
if ($this->sharedStringsManager->hasSharedStrings()) {
|
||||
// Extracts all the strings from the sheets for easy access in the future
|
||||
$this->sharedStringsManager->extractSharedStrings();
|
||||
}
|
||||
|
||||
$this->sheetIterator = $entityFactory->createSheetIterator(
|
||||
$filePath,
|
||||
$this->optionsManager,
|
||||
$this->sharedStringsManager
|
||||
);
|
||||
} else {
|
||||
throw new IOException("Could not open {$filePath} for reading.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator to iterate over sheets.
|
||||
*
|
||||
* @return SheetIterator To iterate over sheets
|
||||
*/
|
||||
protected function getConcreteSheetIterator()
|
||||
{
|
||||
return $this->sheetIterator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the reader. To be used after reading the file.
|
||||
*/
|
||||
protected function closeReader()
|
||||
{
|
||||
if (null !== $this->zip) {
|
||||
$this->zip->close();
|
||||
}
|
||||
|
||||
if (null !== $this->sharedStringsManager) {
|
||||
$this->sharedStringsManager->cleanup();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Reader\XLSX;
|
||||
|
||||
use OpenSpout\Reader\SheetInterface;
|
||||
|
||||
/**
|
||||
* Represents a sheet within a XLSX file.
|
||||
*/
|
||||
class Sheet implements SheetInterface
|
||||
{
|
||||
/** @var \OpenSpout\Reader\XLSX\RowIterator To iterate over sheet's rows */
|
||||
protected $rowIterator;
|
||||
|
||||
/** @var int Index of the sheet, based on order in the workbook (zero-based) */
|
||||
protected $index;
|
||||
|
||||
/** @var string Name of the sheet */
|
||||
protected $name;
|
||||
|
||||
/** @var bool Whether the sheet was the active one */
|
||||
protected $isActive;
|
||||
|
||||
/** @var bool Whether the sheet is visible */
|
||||
protected $isVisible;
|
||||
|
||||
/**
|
||||
* @param RowIterator $rowIterator The corresponding row iterator
|
||||
* @param int $sheetIndex Index of the sheet, based on order in the workbook (zero-based)
|
||||
* @param string $sheetName Name of the sheet
|
||||
* @param bool $isSheetActive Whether the sheet was defined as active
|
||||
* @param bool $isSheetVisible Whether the sheet is visible
|
||||
*/
|
||||
public function __construct($rowIterator, $sheetIndex, $sheetName, $isSheetActive, $isSheetVisible)
|
||||
{
|
||||
$this->rowIterator = $rowIterator;
|
||||
$this->index = $sheetIndex;
|
||||
$this->name = $sheetName;
|
||||
$this->isActive = $isSheetActive;
|
||||
$this->isVisible = $isSheetVisible;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \OpenSpout\Reader\XLSX\RowIterator
|
||||
*/
|
||||
public function getRowIterator()
|
||||
{
|
||||
return $this->rowIterator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int Index of the sheet, based on order in the workbook (zero-based)
|
||||
*/
|
||||
public function getIndex()
|
||||
{
|
||||
return $this->index;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string Name of the sheet
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool Whether the sheet was defined as active
|
||||
*/
|
||||
public function isActive()
|
||||
{
|
||||
return $this->isActive;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool Whether the sheet is visible
|
||||
*/
|
||||
public function isVisible()
|
||||
{
|
||||
return $this->isVisible;
|
||||
}
|
||||
}
|
@ -1,113 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Reader\XLSX;
|
||||
|
||||
use OpenSpout\Reader\Exception\NoSheetsFoundException;
|
||||
use OpenSpout\Reader\IteratorInterface;
|
||||
use OpenSpout\Reader\XLSX\Manager\SheetManager;
|
||||
|
||||
/**
|
||||
* Iterate over XLSX sheet.
|
||||
*/
|
||||
class SheetIterator implements IteratorInterface
|
||||
{
|
||||
/** @var \OpenSpout\Reader\XLSX\Sheet[] The list of sheet present in the file */
|
||||
protected $sheets;
|
||||
|
||||
/** @var int The index of the sheet being read (zero-based) */
|
||||
protected $currentSheetIndex;
|
||||
|
||||
/**
|
||||
* @param SheetManager $sheetManager Manages sheets
|
||||
*
|
||||
* @throws \OpenSpout\Reader\Exception\NoSheetsFoundException If there are no sheets in the file
|
||||
*/
|
||||
public function __construct($sheetManager)
|
||||
{
|
||||
// Fetch all available sheets
|
||||
$this->sheets = $sheetManager->getSheets();
|
||||
|
||||
if (0 === \count($this->sheets)) {
|
||||
throw new NoSheetsFoundException('The file must contain at least one sheet.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewind the Iterator to the first element.
|
||||
*
|
||||
* @see http://php.net/manual/en/iterator.rewind.php
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function rewind()
|
||||
{
|
||||
$this->currentSheetIndex = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if current position is valid.
|
||||
*
|
||||
* @see http://php.net/manual/en/iterator.valid.php
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function valid()
|
||||
{
|
||||
return $this->currentSheetIndex < \count($this->sheets);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move forward to next element.
|
||||
*
|
||||
* @see http://php.net/manual/en/iterator.next.php
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function next()
|
||||
{
|
||||
// Using isset here because it is way faster than array_key_exists...
|
||||
if (isset($this->sheets[$this->currentSheetIndex])) {
|
||||
$currentSheet = $this->sheets[$this->currentSheetIndex];
|
||||
$currentSheet->getRowIterator()->end();
|
||||
|
||||
++$this->currentSheetIndex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current element.
|
||||
*
|
||||
* @see http://php.net/manual/en/iterator.current.php
|
||||
*
|
||||
* @return \OpenSpout\Reader\XLSX\Sheet
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function current()
|
||||
{
|
||||
return $this->sheets[$this->currentSheetIndex];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the key of the current element.
|
||||
*
|
||||
* @see http://php.net/manual/en/iterator.key.php
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function key()
|
||||
{
|
||||
return $this->currentSheetIndex + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up what was created to iterate over the object.
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function end()
|
||||
{
|
||||
// make sure we are not leaking memory in case the iteration stopped before the end
|
||||
foreach ($this->sheets as $sheet) {
|
||||
$sheet->getRowIterator()->end();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Writer\CSV\Manager;
|
||||
|
||||
use OpenSpout\Common\Manager\OptionsManagerAbstract;
|
||||
use OpenSpout\Writer\Common\Entity\Options;
|
||||
|
||||
/**
|
||||
* CSV Writer options manager.
|
||||
*/
|
||||
class OptionsManager extends OptionsManagerAbstract
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getSupportedOptions()
|
||||
{
|
||||
return [
|
||||
Options::FIELD_DELIMITER,
|
||||
Options::FIELD_ENCLOSURE,
|
||||
Options::SHOULD_ADD_BOM,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setDefaultOptions()
|
||||
{
|
||||
$this->setOption(Options::FIELD_DELIMITER, ',');
|
||||
$this->setOption(Options::FIELD_ENCLOSURE, '"');
|
||||
$this->setOption(Options::SHOULD_ADD_BOM, true);
|
||||
}
|
||||
}
|
@ -1,109 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Writer\CSV;
|
||||
|
||||
use OpenSpout\Common\Entity\Row;
|
||||
use OpenSpout\Common\Exception\IOException;
|
||||
use OpenSpout\Common\Helper\EncodingHelper;
|
||||
use OpenSpout\Writer\Common\Entity\Options;
|
||||
use OpenSpout\Writer\WriterAbstract;
|
||||
|
||||
/**
|
||||
* This class provides support to write data to CSV files.
|
||||
*/
|
||||
class Writer extends WriterAbstract
|
||||
{
|
||||
/** Number of rows to write before flushing */
|
||||
public const FLUSH_THRESHOLD = 500;
|
||||
|
||||
/** @var string Content-Type value for the header */
|
||||
protected static $headerContentType = 'text/csv; charset=UTF-8';
|
||||
|
||||
/** @var int */
|
||||
protected $lastWrittenRowIndex = 0;
|
||||
|
||||
/**
|
||||
* Sets the field delimiter for the CSV.
|
||||
*
|
||||
* @param string $fieldDelimiter Character that delimits fields
|
||||
*
|
||||
* @return Writer
|
||||
*/
|
||||
public function setFieldDelimiter($fieldDelimiter)
|
||||
{
|
||||
$this->optionsManager->setOption(Options::FIELD_DELIMITER, $fieldDelimiter);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the field enclosure for the CSV.
|
||||
*
|
||||
* @param string $fieldEnclosure Character that enclose fields
|
||||
*
|
||||
* @return Writer
|
||||
*/
|
||||
public function setFieldEnclosure($fieldEnclosure)
|
||||
{
|
||||
$this->optionsManager->setOption(Options::FIELD_ENCLOSURE, $fieldEnclosure);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set if a BOM has to be added to the file.
|
||||
*
|
||||
* @param bool $shouldAddBOM
|
||||
*
|
||||
* @return Writer
|
||||
*/
|
||||
public function setShouldAddBOM($shouldAddBOM)
|
||||
{
|
||||
$this->optionsManager->setOption(Options::SHOULD_ADD_BOM, (bool) $shouldAddBOM);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the CSV streamer and makes it ready to accept data.
|
||||
*/
|
||||
protected function openWriter()
|
||||
{
|
||||
if ($this->optionsManager->getOption(Options::SHOULD_ADD_BOM)) {
|
||||
// Adds UTF-8 BOM for Unicode compatibility
|
||||
$this->globalFunctionsHelper->fputs($this->filePointer, EncodingHelper::BOM_UTF8);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a row to the currently opened writer.
|
||||
*
|
||||
* @param Row $row The row containing cells and styles
|
||||
*
|
||||
* @throws IOException If unable to write data
|
||||
*/
|
||||
protected function addRowToWriter(Row $row)
|
||||
{
|
||||
$fieldDelimiter = $this->optionsManager->getOption(Options::FIELD_DELIMITER);
|
||||
$fieldEnclosure = $this->optionsManager->getOption(Options::FIELD_ENCLOSURE);
|
||||
|
||||
$wasWriteSuccessful = $this->globalFunctionsHelper->fputcsv($this->filePointer, $row->getCells(), $fieldDelimiter, $fieldEnclosure);
|
||||
if (false === $wasWriteSuccessful) {
|
||||
throw new IOException('Unable to write data');
|
||||
}
|
||||
|
||||
++$this->lastWrittenRowIndex;
|
||||
if (0 === $this->lastWrittenRowIndex % self::FLUSH_THRESHOLD) {
|
||||
$this->globalFunctionsHelper->fflush($this->filePointer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the CSV streamer, preventing any additional writing.
|
||||
* If set, sets the headers and redirects output to the browser.
|
||||
*/
|
||||
protected function closeWriter()
|
||||
{
|
||||
$this->lastWrittenRowIndex = 0;
|
||||
}
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Writer\Common\Creator;
|
||||
|
||||
use OpenSpout\Writer\Common\Entity\Sheet;
|
||||
use OpenSpout\Writer\Common\Entity\Workbook;
|
||||
use OpenSpout\Writer\Common\Entity\Worksheet;
|
||||
use OpenSpout\Writer\Common\Manager\SheetManager;
|
||||
|
||||
/**
|
||||
* Factory to create internal entities.
|
||||
*/
|
||||
class InternalEntityFactory
|
||||
{
|
||||
/**
|
||||
* @return Workbook
|
||||
*/
|
||||
public function createWorkbook()
|
||||
{
|
||||
return new Workbook();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $worksheetFilePath
|
||||
*
|
||||
* @return Worksheet
|
||||
*/
|
||||
public function createWorksheet($worksheetFilePath, Sheet $externalSheet)
|
||||
{
|
||||
return new Worksheet($worksheetFilePath, $externalSheet);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $sheetIndex Index of the sheet, based on order in the workbook (zero-based)
|
||||
* @param string $associatedWorkbookId ID of the sheet's associated workbook
|
||||
* @param SheetManager $sheetManager To manage sheets
|
||||
*
|
||||
* @return Sheet
|
||||
*/
|
||||
public function createSheet($sheetIndex, $associatedWorkbookId, $sheetManager)
|
||||
{
|
||||
return new Sheet($sheetIndex, $associatedWorkbookId, $sheetManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \ZipArchive
|
||||
*/
|
||||
public function createZipArchive()
|
||||
{
|
||||
return new \ZipArchive();
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Writer\Common\Creator;
|
||||
|
||||
use OpenSpout\Common\Manager\OptionsManagerInterface;
|
||||
use OpenSpout\Writer\Common\Manager\SheetManager;
|
||||
use OpenSpout\Writer\Common\Manager\WorkbookManagerInterface;
|
||||
|
||||
/**
|
||||
* Interface ManagerFactoryInterface.
|
||||
*/
|
||||
interface ManagerFactoryInterface
|
||||
{
|
||||
/**
|
||||
* @return WorkbookManagerInterface
|
||||
*/
|
||||
public function createWorkbookManager(OptionsManagerInterface $optionsManager);
|
||||
|
||||
/**
|
||||
* @return SheetManager
|
||||
*/
|
||||
public function createSheetManager();
|
||||
}
|
@ -1,84 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Writer\Common\Creator\Style;
|
||||
|
||||
use OpenSpout\Common\Entity\Style\Border;
|
||||
use OpenSpout\Common\Entity\Style\BorderPart;
|
||||
use OpenSpout\Common\Entity\Style\Color;
|
||||
|
||||
class BorderBuilder
|
||||
{
|
||||
/**
|
||||
* @var Border
|
||||
*/
|
||||
protected $border;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->border = new Border();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $color Border A RGB color code
|
||||
* @param string $width Border width @see BorderPart::allowedWidths
|
||||
* @param string $style Border style @see BorderPart::allowedStyles
|
||||
*
|
||||
* @return BorderBuilder
|
||||
*/
|
||||
public function setBorderTop($color = Color::BLACK, $width = Border::WIDTH_MEDIUM, $style = Border::STYLE_SOLID)
|
||||
{
|
||||
$this->border->addPart(new BorderPart(Border::TOP, $color, $width, $style));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $color Border A RGB color code
|
||||
* @param string $width Border width @see BorderPart::allowedWidths
|
||||
* @param string $style Border style @see BorderPart::allowedStyles
|
||||
*
|
||||
* @return BorderBuilder
|
||||
*/
|
||||
public function setBorderRight($color = Color::BLACK, $width = Border::WIDTH_MEDIUM, $style = Border::STYLE_SOLID)
|
||||
{
|
||||
$this->border->addPart(new BorderPart(Border::RIGHT, $color, $width, $style));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $color Border A RGB color code
|
||||
* @param string $width Border width @see BorderPart::allowedWidths
|
||||
* @param string $style Border style @see BorderPart::allowedStyles
|
||||
*
|
||||
* @return BorderBuilder
|
||||
*/
|
||||
public function setBorderBottom($color = Color::BLACK, $width = Border::WIDTH_MEDIUM, $style = Border::STYLE_SOLID)
|
||||
{
|
||||
$this->border->addPart(new BorderPart(Border::BOTTOM, $color, $width, $style));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $color Border A RGB color code
|
||||
* @param string $width Border width @see BorderPart::allowedWidths
|
||||
* @param string $style Border style @see BorderPart::allowedStyles
|
||||
*
|
||||
* @return BorderBuilder
|
||||
*/
|
||||
public function setBorderLeft($color = Color::BLACK, $width = Border::WIDTH_MEDIUM, $style = Border::STYLE_SOLID)
|
||||
{
|
||||
$this->border->addPart(new BorderPart(Border::LEFT, $color, $width, $style));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Border
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
return $this->border;
|
||||
}
|
||||
}
|
@ -1,214 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Writer\Common\Creator\Style;
|
||||
|
||||
use OpenSpout\Common\Entity\Style\Border;
|
||||
use OpenSpout\Common\Entity\Style\CellAlignment;
|
||||
use OpenSpout\Common\Entity\Style\Style;
|
||||
use OpenSpout\Common\Exception\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Builder to create new styles.
|
||||
*/
|
||||
class StyleBuilder
|
||||
{
|
||||
/** @var Style Style to be created */
|
||||
protected $style;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->style = new Style();
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the font bold.
|
||||
*
|
||||
* @return StyleBuilder
|
||||
*/
|
||||
public function setFontBold()
|
||||
{
|
||||
$this->style->setFontBold();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the font italic.
|
||||
*
|
||||
* @return StyleBuilder
|
||||
*/
|
||||
public function setFontItalic()
|
||||
{
|
||||
$this->style->setFontItalic();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the font underlined.
|
||||
*
|
||||
* @return StyleBuilder
|
||||
*/
|
||||
public function setFontUnderline()
|
||||
{
|
||||
$this->style->setFontUnderline();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the font struck through.
|
||||
*
|
||||
* @return StyleBuilder
|
||||
*/
|
||||
public function setFontStrikethrough()
|
||||
{
|
||||
$this->style->setFontStrikethrough();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the font size.
|
||||
*
|
||||
* @param int $fontSize Font size, in pixels
|
||||
*
|
||||
* @return StyleBuilder
|
||||
*/
|
||||
public function setFontSize($fontSize)
|
||||
{
|
||||
$this->style->setFontSize($fontSize);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the font color.
|
||||
*
|
||||
* @param string $fontColor ARGB color (@see Color)
|
||||
*
|
||||
* @return StyleBuilder
|
||||
*/
|
||||
public function setFontColor($fontColor)
|
||||
{
|
||||
$this->style->setFontColor($fontColor);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the font name.
|
||||
*
|
||||
* @param string $fontName Name of the font to use
|
||||
*
|
||||
* @return StyleBuilder
|
||||
*/
|
||||
public function setFontName($fontName)
|
||||
{
|
||||
$this->style->setFontName($fontName);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the text wrap in the cell if requested.
|
||||
*
|
||||
* @param bool $shouldWrap Should the text be wrapped
|
||||
*
|
||||
* @return StyleBuilder
|
||||
*/
|
||||
public function setShouldWrapText($shouldWrap = true)
|
||||
{
|
||||
$this->style->setShouldWrapText($shouldWrap);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cell alignment.
|
||||
*
|
||||
* @param string $cellAlignment The cell alignment
|
||||
*
|
||||
* @throws InvalidArgumentException If the given cell alignment is not valid
|
||||
*
|
||||
* @return StyleBuilder
|
||||
*/
|
||||
public function setCellAlignment($cellAlignment)
|
||||
{
|
||||
if (!CellAlignment::isValid($cellAlignment)) {
|
||||
throw new InvalidArgumentException('Invalid cell alignment value');
|
||||
}
|
||||
|
||||
$this->style->setCellAlignment($cellAlignment);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a border.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setBorder(Border $border)
|
||||
{
|
||||
$this->style->setBorder($border);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a background color.
|
||||
*
|
||||
* @param string $color ARGB color (@see Color)
|
||||
*
|
||||
* @return StyleBuilder
|
||||
*/
|
||||
public function setBackgroundColor($color)
|
||||
{
|
||||
$this->style->setBackgroundColor($color);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a format.
|
||||
*
|
||||
* @param string $format Format
|
||||
*
|
||||
* @return StyleBuilder
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function setFormat($format)
|
||||
{
|
||||
$this->style->setFormat($format);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set should shrink to fit.
|
||||
*
|
||||
* @param bool $shrinkToFit
|
||||
*
|
||||
* @return StyleBuilder
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function setShouldShrinkToFit($shrinkToFit = true)
|
||||
{
|
||||
$this->style->setShouldShrinkToFit($shrinkToFit);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the configured style. The style is cached and can be reused.
|
||||
*
|
||||
* @return Style
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
return $this->style;
|
||||
}
|
||||
}
|
@ -1,121 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Writer\Common\Creator;
|
||||
|
||||
use OpenSpout\Common\Entity\Cell;
|
||||
use OpenSpout\Common\Entity\Row;
|
||||
use OpenSpout\Common\Entity\Style\Style;
|
||||
use OpenSpout\Common\Exception\UnsupportedTypeException;
|
||||
use OpenSpout\Common\Type;
|
||||
use OpenSpout\Writer\WriterInterface;
|
||||
|
||||
/**
|
||||
* Factory to create external entities.
|
||||
*/
|
||||
class WriterEntityFactory
|
||||
{
|
||||
/**
|
||||
* This creates an instance of the appropriate writer, given the type of the file to be written.
|
||||
*
|
||||
* @param string $writerType Type of the writer to instantiate
|
||||
*
|
||||
* @throws \OpenSpout\Common\Exception\UnsupportedTypeException
|
||||
*
|
||||
* @return WriterInterface
|
||||
*/
|
||||
public static function createWriter($writerType)
|
||||
{
|
||||
return WriterFactory::createFromType($writerType);
|
||||
}
|
||||
|
||||
/**
|
||||
* This creates an instance of the appropriate writer, given the extension of the file to be written.
|
||||
*
|
||||
* @param string $path The path to the spreadsheet file. Supported extensions are .csv, .ods and .xlsx
|
||||
*
|
||||
* @throws \OpenSpout\Common\Exception\UnsupportedTypeException
|
||||
*
|
||||
* @return WriterInterface
|
||||
*/
|
||||
public static function createWriterFromFile(string $path)
|
||||
{
|
||||
return WriterFactory::createFromFile($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* This creates an instance of a CSV writer.
|
||||
*
|
||||
* @return \OpenSpout\Writer\CSV\Writer
|
||||
*/
|
||||
public static function createCSVWriter()
|
||||
{
|
||||
try {
|
||||
return WriterFactory::createFromType(Type::CSV);
|
||||
} catch (UnsupportedTypeException $e) {
|
||||
// should never happen
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This creates an instance of a XLSX writer.
|
||||
*
|
||||
* @return \OpenSpout\Writer\XLSX\Writer
|
||||
*/
|
||||
public static function createXLSXWriter()
|
||||
{
|
||||
try {
|
||||
return WriterFactory::createFromType(Type::XLSX);
|
||||
} catch (UnsupportedTypeException $e) {
|
||||
// should never happen
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This creates an instance of a ODS writer.
|
||||
*
|
||||
* @return \OpenSpout\Writer\ODS\Writer
|
||||
*/
|
||||
public static function createODSWriter()
|
||||
{
|
||||
try {
|
||||
return WriterFactory::createFromType(Type::ODS);
|
||||
} catch (UnsupportedTypeException $e) {
|
||||
// should never happen
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Cell[] $cells
|
||||
*
|
||||
* @return Row
|
||||
*/
|
||||
public static function createRow(array $cells = [], Style $rowStyle = null)
|
||||
{
|
||||
return new Row($cells, $rowStyle);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Row
|
||||
*/
|
||||
public static function createRowFromArray(array $cellValues = [], Style $rowStyle = null)
|
||||
{
|
||||
$cells = array_map(function ($cellValue) {
|
||||
return new Cell($cellValue);
|
||||
}, $cellValues);
|
||||
|
||||
return new Row($cells, $rowStyle);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $cellValue
|
||||
*
|
||||
* @return Cell
|
||||
*/
|
||||
public static function createCell($cellValue, Style $cellStyle = null)
|
||||
{
|
||||
return new Cell($cellValue, $cellStyle);
|
||||
}
|
||||
}
|
@ -1,109 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Writer\Common\Creator;
|
||||
|
||||
use OpenSpout\Common\Creator\HelperFactory;
|
||||
use OpenSpout\Common\Exception\UnsupportedTypeException;
|
||||
use OpenSpout\Common\Helper\GlobalFunctionsHelper;
|
||||
use OpenSpout\Common\Type;
|
||||
use OpenSpout\Writer\Common\Creator\Style\StyleBuilder;
|
||||
use OpenSpout\Writer\CSV\Manager\OptionsManager as CSVOptionsManager;
|
||||
use OpenSpout\Writer\CSV\Writer as CSVWriter;
|
||||
use OpenSpout\Writer\ODS\Creator\HelperFactory as ODSHelperFactory;
|
||||
use OpenSpout\Writer\ODS\Creator\ManagerFactory as ODSManagerFactory;
|
||||
use OpenSpout\Writer\ODS\Manager\OptionsManager as ODSOptionsManager;
|
||||
use OpenSpout\Writer\ODS\Writer as ODSWriter;
|
||||
use OpenSpout\Writer\WriterInterface;
|
||||
use OpenSpout\Writer\XLSX\Creator\HelperFactory as XLSXHelperFactory;
|
||||
use OpenSpout\Writer\XLSX\Creator\ManagerFactory as XLSXManagerFactory;
|
||||
use OpenSpout\Writer\XLSX\Manager\OptionsManager as XLSXOptionsManager;
|
||||
use OpenSpout\Writer\XLSX\Writer as XLSXWriter;
|
||||
|
||||
/**
|
||||
* This factory is used to create writers, based on the type of the file to be read.
|
||||
* It supports CSV, XLSX and ODS formats.
|
||||
*/
|
||||
class WriterFactory
|
||||
{
|
||||
/**
|
||||
* This creates an instance of the appropriate writer, given the extension of the file to be written.
|
||||
*
|
||||
* @param string $path The path to the spreadsheet file. Supported extensions are .csv,.ods and .xlsx
|
||||
*
|
||||
* @throws \OpenSpout\Common\Exception\UnsupportedTypeException
|
||||
*
|
||||
* @return WriterInterface
|
||||
*/
|
||||
public static function createFromFile(string $path)
|
||||
{
|
||||
$extension = strtolower(pathinfo($path, PATHINFO_EXTENSION));
|
||||
|
||||
return self::createFromType($extension);
|
||||
}
|
||||
|
||||
/**
|
||||
* This creates an instance of the appropriate writer, given the type of the file to be written.
|
||||
*
|
||||
* @param string $writerType Type of the writer to instantiate
|
||||
*
|
||||
* @throws \OpenSpout\Common\Exception\UnsupportedTypeException
|
||||
*
|
||||
* @return WriterInterface
|
||||
*/
|
||||
public static function createFromType($writerType)
|
||||
{
|
||||
switch ($writerType) {
|
||||
case Type::CSV: return self::createCSVWriter();
|
||||
|
||||
case Type::XLSX: return self::createXLSXWriter();
|
||||
|
||||
case Type::ODS: return self::createODSWriter();
|
||||
|
||||
default:
|
||||
throw new UnsupportedTypeException('No writers supporting the given type: '.$writerType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CSVWriter
|
||||
*/
|
||||
private static function createCSVWriter()
|
||||
{
|
||||
$optionsManager = new CSVOptionsManager();
|
||||
$globalFunctionsHelper = new GlobalFunctionsHelper();
|
||||
|
||||
$helperFactory = new HelperFactory();
|
||||
|
||||
return new CSVWriter($optionsManager, $globalFunctionsHelper, $helperFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return XLSXWriter
|
||||
*/
|
||||
private static function createXLSXWriter()
|
||||
{
|
||||
$styleBuilder = new StyleBuilder();
|
||||
$optionsManager = new XLSXOptionsManager($styleBuilder);
|
||||
$globalFunctionsHelper = new GlobalFunctionsHelper();
|
||||
|
||||
$helperFactory = new XLSXHelperFactory();
|
||||
$managerFactory = new XLSXManagerFactory(new InternalEntityFactory(), $helperFactory);
|
||||
|
||||
return new XLSXWriter($optionsManager, $globalFunctionsHelper, $helperFactory, $managerFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ODSWriter
|
||||
*/
|
||||
private static function createODSWriter()
|
||||
{
|
||||
$styleBuilder = new StyleBuilder();
|
||||
$optionsManager = new ODSOptionsManager($styleBuilder);
|
||||
$globalFunctionsHelper = new GlobalFunctionsHelper();
|
||||
|
||||
$helperFactory = new ODSHelperFactory();
|
||||
$managerFactory = new ODSManagerFactory(new InternalEntityFactory(), $helperFactory);
|
||||
|
||||
return new ODSWriter($optionsManager, $globalFunctionsHelper, $helperFactory, $managerFactory);
|
||||
}
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Writer\Common\Entity;
|
||||
|
||||
/**
|
||||
* Writers' options holder.
|
||||
*/
|
||||
abstract class Options
|
||||
{
|
||||
// CSV specific options
|
||||
public const FIELD_DELIMITER = 'fieldDelimiter';
|
||||
public const FIELD_ENCLOSURE = 'fieldEnclosure';
|
||||
public const SHOULD_ADD_BOM = 'shouldAddBOM';
|
||||
|
||||
// Multisheets options
|
||||
public const TEMP_FOLDER = 'tempFolder';
|
||||
public const DEFAULT_ROW_STYLE = 'defaultRowStyle';
|
||||
public const SHOULD_CREATE_NEW_SHEETS_AUTOMATICALLY = 'shouldCreateNewSheetsAutomatically';
|
||||
|
||||
// XLSX specific options
|
||||
public const SHOULD_USE_INLINE_STRINGS = 'shouldUseInlineStrings';
|
||||
|
||||
// Cell size options
|
||||
public const DEFAULT_COLUMN_WIDTH = 'defaultColumnWidth';
|
||||
public const DEFAULT_ROW_HEIGHT = 'defaultRowHeight';
|
||||
public const COLUMN_WIDTHS = 'columnWidthDefinition';
|
||||
|
||||
// XLSX merge cells
|
||||
public const MERGE_CELLS = 'mergeCells';
|
||||
}
|
@ -1,137 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Writer\Common\Entity;
|
||||
|
||||
use OpenSpout\Writer\Common\Manager\SheetManager;
|
||||
use OpenSpout\Writer\XLSX\Entity\SheetView;
|
||||
|
||||
/**
|
||||
* External representation of a worksheet.
|
||||
*/
|
||||
class Sheet
|
||||
{
|
||||
public const DEFAULT_SHEET_NAME_PREFIX = 'Sheet';
|
||||
|
||||
/** @var int Index of the sheet, based on order in the workbook (zero-based) */
|
||||
private $index;
|
||||
|
||||
/** @var string ID of the sheet's associated workbook. Used to restrict sheet name uniqueness enforcement to a single workbook */
|
||||
private $associatedWorkbookId;
|
||||
|
||||
/** @var string Name of the sheet */
|
||||
private $name;
|
||||
|
||||
/** @var bool Visibility of the sheet */
|
||||
private $isVisible;
|
||||
|
||||
/** @var SheetManager Sheet manager */
|
||||
private $sheetManager;
|
||||
|
||||
/** @var SheetView */
|
||||
private $sheetView;
|
||||
|
||||
/**
|
||||
* @param int $sheetIndex Index of the sheet, based on order in the workbook (zero-based)
|
||||
* @param string $associatedWorkbookId ID of the sheet's associated workbook
|
||||
* @param SheetManager $sheetManager To manage sheets
|
||||
*/
|
||||
public function __construct($sheetIndex, $associatedWorkbookId, SheetManager $sheetManager)
|
||||
{
|
||||
$this->index = $sheetIndex;
|
||||
$this->associatedWorkbookId = $associatedWorkbookId;
|
||||
|
||||
$this->sheetManager = $sheetManager;
|
||||
$this->sheetManager->markWorkbookIdAsUsed($associatedWorkbookId);
|
||||
|
||||
$this->setName(self::DEFAULT_SHEET_NAME_PREFIX.($sheetIndex + 1));
|
||||
$this->setIsVisible(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int Index of the sheet, based on order in the workbook (zero-based)
|
||||
*/
|
||||
public function getIndex()
|
||||
{
|
||||
return $this->index;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getAssociatedWorkbookId()
|
||||
{
|
||||
return $this->associatedWorkbookId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string Name of the sheet
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of the sheet. Note that Excel has some restrictions on the name:
|
||||
* - it should not be blank
|
||||
* - it should not exceed 31 characters
|
||||
* - it should not contain these characters: \ / ? * : [ or ]
|
||||
* - it should be unique.
|
||||
*
|
||||
* @param string $name Name of the sheet
|
||||
*
|
||||
* @throws \OpenSpout\Writer\Exception\InvalidSheetNameException if the sheet's name is invalid
|
||||
*
|
||||
* @return Sheet
|
||||
*/
|
||||
public function setName($name)
|
||||
{
|
||||
$this->sheetManager->throwIfNameIsInvalid($name, $this);
|
||||
|
||||
$this->name = $name;
|
||||
|
||||
$this->sheetManager->markSheetNameAsUsed($this);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool isVisible Visibility of the sheet
|
||||
*/
|
||||
public function isVisible()
|
||||
{
|
||||
return $this->isVisible;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $isVisible Visibility of the sheet
|
||||
*
|
||||
* @return Sheet
|
||||
*/
|
||||
public function setIsVisible($isVisible)
|
||||
{
|
||||
$this->isVisible = $isVisible;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSheetView(): ?SheetView
|
||||
{
|
||||
return $this->sheetView;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function setSheetView(SheetView $sheetView)
|
||||
{
|
||||
$this->sheetView = $sheetView;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function hasSheetView(): bool
|
||||
{
|
||||
return $this->sheetView instanceof SheetView;
|
||||
}
|
||||
}
|
@ -1,131 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Writer\Common\Entity;
|
||||
|
||||
/**
|
||||
* Entity describing a Worksheet.
|
||||
*/
|
||||
class Worksheet
|
||||
{
|
||||
/** @var string Path to the XML file that will contain the sheet data */
|
||||
private $filePath;
|
||||
|
||||
/** @var null|resource Pointer to the sheet data file (e.g. xl/worksheets/sheet1.xml) */
|
||||
private $filePointer;
|
||||
|
||||
/** @var Sheet The "external" sheet */
|
||||
private $externalSheet;
|
||||
|
||||
/** @var int Maximum number of columns among all the written rows */
|
||||
private $maxNumColumns;
|
||||
|
||||
/** @var int Index of the last written row */
|
||||
private $lastWrittenRowIndex;
|
||||
|
||||
/** @var bool has the sheet data header been written */
|
||||
private $sheetDataStarted = false;
|
||||
|
||||
/**
|
||||
* Worksheet constructor.
|
||||
*
|
||||
* @param string $worksheetFilePath
|
||||
*/
|
||||
public function __construct($worksheetFilePath, Sheet $externalSheet)
|
||||
{
|
||||
$this->filePath = $worksheetFilePath;
|
||||
$this->filePointer = null;
|
||||
$this->externalSheet = $externalSheet;
|
||||
$this->maxNumColumns = 0;
|
||||
$this->lastWrittenRowIndex = 0;
|
||||
$this->sheetDataStarted = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getFilePath()
|
||||
{
|
||||
return $this->filePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return resource
|
||||
*/
|
||||
public function getFilePointer()
|
||||
{
|
||||
return $this->filePointer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resource $filePointer
|
||||
*/
|
||||
public function setFilePointer($filePointer)
|
||||
{
|
||||
$this->filePointer = $filePointer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Sheet
|
||||
*/
|
||||
public function getExternalSheet()
|
||||
{
|
||||
return $this->externalSheet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getMaxNumColumns()
|
||||
{
|
||||
return $this->maxNumColumns;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $maxNumColumns
|
||||
*/
|
||||
public function setMaxNumColumns($maxNumColumns)
|
||||
{
|
||||
$this->maxNumColumns = $maxNumColumns;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getLastWrittenRowIndex()
|
||||
{
|
||||
return $this->lastWrittenRowIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $lastWrittenRowIndex
|
||||
*/
|
||||
public function setLastWrittenRowIndex($lastWrittenRowIndex)
|
||||
{
|
||||
$this->lastWrittenRowIndex = $lastWrittenRowIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int The ID of the worksheet
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
// sheet index is zero-based, while ID is 1-based
|
||||
return $this->externalSheet->getIndex() + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function getSheetDataStarted()
|
||||
{
|
||||
return $this->sheetDataStarted;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $sheetDataStarted
|
||||
*/
|
||||
public function setSheetDataStarted($sheetDataStarted)
|
||||
{
|
||||
$this->sheetDataStarted = $sheetDataStarted;
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Writer\Common\Helper;
|
||||
|
||||
use OpenSpout\Common\Helper\FileSystemHelperInterface;
|
||||
|
||||
/**
|
||||
* This interface describes helper functions to help with the file system operations
|
||||
* like files/folders creation & deletion.
|
||||
*/
|
||||
interface FileSystemWithRootFolderHelperInterface extends FileSystemHelperInterface
|
||||
{
|
||||
/**
|
||||
* Creates all the folders needed to create a spreadsheet, as well as the files that won't change.
|
||||
*
|
||||
* @throws \OpenSpout\Common\Exception\IOException If unable to create at least one of the base folders
|
||||
*/
|
||||
public function createBaseFilesAndFolders();
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getRootFolder();
|
||||
}
|
@ -1,214 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Writer\Common\Helper;
|
||||
|
||||
use OpenSpout\Writer\Common\Creator\InternalEntityFactory;
|
||||
|
||||
/**
|
||||
* This class provides helper functions to create zip files.
|
||||
*/
|
||||
class ZipHelper
|
||||
{
|
||||
public const ZIP_EXTENSION = '.zip';
|
||||
|
||||
/** Controls what to do when trying to add an existing file */
|
||||
public const EXISTING_FILES_SKIP = 'skip';
|
||||
public const EXISTING_FILES_OVERWRITE = 'overwrite';
|
||||
|
||||
/** @var InternalEntityFactory Factory to create entities */
|
||||
private $entityFactory;
|
||||
|
||||
/**
|
||||
* @param InternalEntityFactory $entityFactory Factory to create entities
|
||||
*/
|
||||
public function __construct($entityFactory)
|
||||
{
|
||||
$this->entityFactory = $entityFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new ZipArchive instance pointing at the given path.
|
||||
*
|
||||
* @param string $tmpFolderPath Path of the temp folder where the zip file will be created
|
||||
*
|
||||
* @return \ZipArchive
|
||||
*/
|
||||
public function createZip($tmpFolderPath)
|
||||
{
|
||||
$zip = $this->entityFactory->createZipArchive();
|
||||
$zipFilePath = $tmpFolderPath.self::ZIP_EXTENSION;
|
||||
|
||||
$zip->open($zipFilePath, \ZipArchive::CREATE | \ZipArchive::OVERWRITE);
|
||||
|
||||
return $zip;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \ZipArchive $zip An opened zip archive object
|
||||
*
|
||||
* @return string Path where the zip file of the given folder will be created
|
||||
*/
|
||||
public function getZipFilePath(\ZipArchive $zip)
|
||||
{
|
||||
return $zip->filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given file, located under the given root folder to the archive.
|
||||
* The file will be compressed.
|
||||
*
|
||||
* Example of use:
|
||||
* addFileToArchive($zip, '/tmp/xlsx/foo', 'bar/baz.xml');
|
||||
* => will add the file located at '/tmp/xlsx/foo/bar/baz.xml' in the archive, but only as 'bar/baz.xml'
|
||||
*
|
||||
* @param \ZipArchive $zip An opened zip archive object
|
||||
* @param string $rootFolderPath path of the root folder that will be ignored in the archive tree
|
||||
* @param string $localFilePath Path of the file to be added, under the root folder
|
||||
* @param string $existingFileMode Controls what to do when trying to add an existing file
|
||||
*/
|
||||
public function addFileToArchive($zip, $rootFolderPath, $localFilePath, $existingFileMode = self::EXISTING_FILES_OVERWRITE)
|
||||
{
|
||||
$this->addFileToArchiveWithCompressionMethod(
|
||||
$zip,
|
||||
$rootFolderPath,
|
||||
$localFilePath,
|
||||
$existingFileMode,
|
||||
\ZipArchive::CM_DEFAULT
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given file, located under the given root folder to the archive.
|
||||
* The file will NOT be compressed.
|
||||
*
|
||||
* Example of use:
|
||||
* addUncompressedFileToArchive($zip, '/tmp/xlsx/foo', 'bar/baz.xml');
|
||||
* => will add the file located at '/tmp/xlsx/foo/bar/baz.xml' in the archive, but only as 'bar/baz.xml'
|
||||
*
|
||||
* @param \ZipArchive $zip An opened zip archive object
|
||||
* @param string $rootFolderPath path of the root folder that will be ignored in the archive tree
|
||||
* @param string $localFilePath Path of the file to be added, under the root folder
|
||||
* @param string $existingFileMode Controls what to do when trying to add an existing file
|
||||
*/
|
||||
public function addUncompressedFileToArchive($zip, $rootFolderPath, $localFilePath, $existingFileMode = self::EXISTING_FILES_OVERWRITE)
|
||||
{
|
||||
$this->addFileToArchiveWithCompressionMethod(
|
||||
$zip,
|
||||
$rootFolderPath,
|
||||
$localFilePath,
|
||||
$existingFileMode,
|
||||
\ZipArchive::CM_STORE
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool Whether it is possible to choose the desired compression method to be used
|
||||
*/
|
||||
public static function canChooseCompressionMethod()
|
||||
{
|
||||
// setCompressionName() is a PHP7+ method...
|
||||
return method_exists(new \ZipArchive(), 'setCompressionName');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \ZipArchive $zip An opened zip archive object
|
||||
* @param string $folderPath Path to the folder to be zipped
|
||||
* @param string $existingFileMode Controls what to do when trying to add an existing file
|
||||
*/
|
||||
public function addFolderToArchive($zip, $folderPath, $existingFileMode = self::EXISTING_FILES_OVERWRITE)
|
||||
{
|
||||
$folderRealPath = $this->getNormalizedRealPath($folderPath).'/';
|
||||
$itemIterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($folderPath, \RecursiveDirectoryIterator::SKIP_DOTS), \RecursiveIteratorIterator::SELF_FIRST);
|
||||
|
||||
foreach ($itemIterator as $itemInfo) {
|
||||
$itemRealPath = $this->getNormalizedRealPath($itemInfo->getPathname());
|
||||
$itemLocalPath = str_replace($folderRealPath, '', $itemRealPath);
|
||||
|
||||
if ($itemInfo->isFile() && !$this->shouldSkipFile($zip, $itemLocalPath, $existingFileMode)) {
|
||||
$zip->addFile($itemRealPath, $itemLocalPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the archive and copies it into the given stream.
|
||||
*
|
||||
* @param \ZipArchive $zip An opened zip archive object
|
||||
* @param resource $streamPointer Pointer to the stream to copy the zip
|
||||
*/
|
||||
public function closeArchiveAndCopyToStream($zip, $streamPointer)
|
||||
{
|
||||
$zipFilePath = $zip->filename;
|
||||
$zip->close();
|
||||
|
||||
$this->copyZipToStream($zipFilePath, $streamPointer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given file, located under the given root folder to the archive.
|
||||
* The file will NOT be compressed.
|
||||
*
|
||||
* Example of use:
|
||||
* addUncompressedFileToArchive($zip, '/tmp/xlsx/foo', 'bar/baz.xml');
|
||||
* => will add the file located at '/tmp/xlsx/foo/bar/baz.xml' in the archive, but only as 'bar/baz.xml'
|
||||
*
|
||||
* @param \ZipArchive $zip An opened zip archive object
|
||||
* @param string $rootFolderPath path of the root folder that will be ignored in the archive tree
|
||||
* @param string $localFilePath Path of the file to be added, under the root folder
|
||||
* @param string $existingFileMode Controls what to do when trying to add an existing file
|
||||
* @param int $compressionMethod The compression method
|
||||
*/
|
||||
protected function addFileToArchiveWithCompressionMethod($zip, $rootFolderPath, $localFilePath, $existingFileMode, $compressionMethod)
|
||||
{
|
||||
if (!$this->shouldSkipFile($zip, $localFilePath, $existingFileMode)) {
|
||||
$normalizedFullFilePath = $this->getNormalizedRealPath($rootFolderPath.'/'.$localFilePath);
|
||||
$zip->addFile($normalizedFullFilePath, $localFilePath);
|
||||
|
||||
if (self::canChooseCompressionMethod()) {
|
||||
$zip->setCompressionName($localFilePath, $compressionMethod);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \ZipArchive $zip
|
||||
* @param string $itemLocalPath
|
||||
* @param string $existingFileMode
|
||||
*
|
||||
* @return bool Whether the file should be added to the archive or skipped
|
||||
*/
|
||||
protected function shouldSkipFile($zip, $itemLocalPath, $existingFileMode)
|
||||
{
|
||||
// Skip files if:
|
||||
// - EXISTING_FILES_SKIP mode chosen
|
||||
// - File already exists in the archive
|
||||
return self::EXISTING_FILES_SKIP === $existingFileMode && false !== $zip->locateName($itemLocalPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns canonicalized absolute pathname, containing only forward slashes.
|
||||
*
|
||||
* @param string $path Path to normalize
|
||||
*
|
||||
* @return string Normalized and canonicalized path
|
||||
*/
|
||||
protected function getNormalizedRealPath($path)
|
||||
{
|
||||
$realPath = realpath($path);
|
||||
|
||||
return str_replace(\DIRECTORY_SEPARATOR, '/', $realPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Streams the contents of the zip file into the given stream.
|
||||
*
|
||||
* @param string $zipFilePath Path of the zip file
|
||||
* @param resource $pointer Pointer to the stream to copy the zip
|
||||
*/
|
||||
protected function copyZipToStream($zipFilePath, $pointer)
|
||||
{
|
||||
$zipFilePointer = fopen($zipFilePath, 'r');
|
||||
stream_copy_to_stream($zipFilePointer, $pointer);
|
||||
fclose($zipFilePointer);
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Writer\Common\Manager;
|
||||
|
||||
use OpenSpout\Common\Entity\Cell;
|
||||
use OpenSpout\Common\Entity\Style\Style;
|
||||
use OpenSpout\Writer\Common\Manager\Style\StyleMerger;
|
||||
|
||||
class CellManager
|
||||
{
|
||||
/**
|
||||
* @var StyleMerger
|
||||
*/
|
||||
protected $styleMerger;
|
||||
|
||||
public function __construct(StyleMerger $styleMerger)
|
||||
{
|
||||
$this->styleMerger = $styleMerger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges a Style into a cell's Style.
|
||||
*/
|
||||
public function applyStyle(Cell $cell, Style $style)
|
||||
{
|
||||
$mergedStyle = $this->styleMerger->merge($cell->getStyle(), $style);
|
||||
$cell->setStyle($mergedStyle);
|
||||
}
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Writer\Common\Manager;
|
||||
|
||||
trait ManagesCellSize
|
||||
{
|
||||
/** @var null|float The default column width to use */
|
||||
private $defaultColumnWidth;
|
||||
|
||||
/** @var null|float The default row height to use */
|
||||
private $defaultRowHeight;
|
||||
|
||||
/** @var array Array of min-max-width arrays */
|
||||
private $columnWidths = [];
|
||||
|
||||
/**
|
||||
* @param null|float $width
|
||||
*/
|
||||
public function setDefaultColumnWidth($width)
|
||||
{
|
||||
$this->defaultColumnWidth = $width;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null|float $height
|
||||
*/
|
||||
public function setDefaultRowHeight($height)
|
||||
{
|
||||
$this->defaultRowHeight = $height;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int ...$columns One or more columns with this width
|
||||
*/
|
||||
public function setColumnWidth(float $width, ...$columns)
|
||||
{
|
||||
// Gather sequences
|
||||
$sequence = [];
|
||||
foreach ($columns as $i) {
|
||||
$sequenceLength = \count($sequence);
|
||||
if ($sequenceLength > 0) {
|
||||
$previousValue = $sequence[$sequenceLength - 1];
|
||||
if ($i !== $previousValue + 1) {
|
||||
$this->setColumnWidthForRange($width, $sequence[0], $previousValue);
|
||||
$sequence = [];
|
||||
}
|
||||
}
|
||||
$sequence[] = $i;
|
||||
}
|
||||
$this->setColumnWidthForRange($width, $sequence[0], $sequence[\count($sequence) - 1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param float $width The width to set
|
||||
* @param int $start First column index of the range
|
||||
* @param int $end Last column index of the range
|
||||
*/
|
||||
public function setColumnWidthForRange(float $width, int $start, int $end)
|
||||
{
|
||||
$this->columnWidths[] = [$start, $end, $width];
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Writer\Common\Manager;
|
||||
|
||||
use OpenSpout\Common\Entity\Row;
|
||||
|
||||
class RowManager
|
||||
{
|
||||
/**
|
||||
* Detect whether a row is considered empty.
|
||||
* An empty row has all of its cells empty.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmpty(Row $row)
|
||||
{
|
||||
foreach ($row->getCells() as $cell) {
|
||||
if (!$cell->isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Writer\Common\Manager;
|
||||
|
||||
use OpenSpout\Common\Entity\Row;
|
||||
use OpenSpout\Writer\Common\Entity\Worksheet;
|
||||
|
||||
/**
|
||||
* Interface WorksheetManagerInterface
|
||||
* Inteface for worksheet managers, providing the generic interfaces to work with worksheets.
|
||||
*/
|
||||
interface WorksheetManagerInterface
|
||||
{
|
||||
/**
|
||||
* @param null|float $width
|
||||
*/
|
||||
public function setDefaultColumnWidth($width);
|
||||
|
||||
/**
|
||||
* @param null|float $height
|
||||
*/
|
||||
public function setDefaultRowHeight($height);
|
||||
|
||||
/**
|
||||
* @param int ...$columns One or more columns with this width
|
||||
*/
|
||||
public function setColumnWidth(float $width, ...$columns);
|
||||
|
||||
/**
|
||||
* @param float $width The width to set
|
||||
* @param int $start First column index of the range
|
||||
* @param int $end Last column index of the range
|
||||
*/
|
||||
public function setColumnWidthForRange(float $width, int $start, int $end);
|
||||
|
||||
/**
|
||||
* Adds a row to the worksheet.
|
||||
*
|
||||
* @param Worksheet $worksheet The worksheet to add the row to
|
||||
* @param Row $row The row to be added
|
||||
*
|
||||
* @throws \OpenSpout\Common\Exception\IOException If the data cannot be written
|
||||
* @throws \OpenSpout\Common\Exception\InvalidArgumentException If a cell value's type is not supported
|
||||
*/
|
||||
public function addRow(Worksheet $worksheet, Row $row);
|
||||
|
||||
/**
|
||||
* Prepares the worksheet to accept data.
|
||||
*
|
||||
* @param Worksheet $worksheet The worksheet to start
|
||||
*
|
||||
* @throws \OpenSpout\Common\Exception\IOException If the sheet data file cannot be opened for writing
|
||||
*/
|
||||
public function startSheet(Worksheet $worksheet);
|
||||
|
||||
/**
|
||||
* Closes the worksheet.
|
||||
*/
|
||||
public function close(Worksheet $worksheet);
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Writer\Exception;
|
||||
|
||||
class InvalidSheetNameException extends WriterException
|
||||
{
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Writer\Exception;
|
||||
|
||||
class SheetNotFoundException extends WriterException
|
||||
{
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Writer\Exception;
|
||||
|
||||
class WriterAlreadyOpenedException extends WriterException
|
||||
{
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Writer\Exception;
|
||||
|
||||
use OpenSpout\Common\Exception\SpoutException;
|
||||
|
||||
abstract class WriterException extends SpoutException
|
||||
{
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Writer\Exception;
|
||||
|
||||
class WriterNotOpenedException extends WriterException
|
||||
{
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Writer\ODS\Creator;
|
||||
|
||||
use OpenSpout\Common\Helper\Escaper;
|
||||
use OpenSpout\Common\Helper\StringHelper;
|
||||
use OpenSpout\Common\Manager\OptionsManagerInterface;
|
||||
use OpenSpout\Writer\Common\Creator\InternalEntityFactory;
|
||||
use OpenSpout\Writer\Common\Entity\Options;
|
||||
use OpenSpout\Writer\Common\Helper\ZipHelper;
|
||||
use OpenSpout\Writer\ODS\Helper\FileSystemHelper;
|
||||
|
||||
/**
|
||||
* Factory for helpers needed by the ODS Writer.
|
||||
*/
|
||||
class HelperFactory extends \OpenSpout\Common\Creator\HelperFactory
|
||||
{
|
||||
/**
|
||||
* @return FileSystemHelper
|
||||
*/
|
||||
public function createSpecificFileSystemHelper(OptionsManagerInterface $optionsManager, InternalEntityFactory $entityFactory)
|
||||
{
|
||||
$tempFolder = $optionsManager->getOption(Options::TEMP_FOLDER);
|
||||
$zipHelper = $this->createZipHelper($entityFactory);
|
||||
|
||||
return new FileSystemHelper($tempFolder, $zipHelper);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Escaper\ODS
|
||||
*/
|
||||
public function createStringsEscaper()
|
||||
{
|
||||
return new Escaper\ODS();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return StringHelper
|
||||
*/
|
||||
public function createStringHelper()
|
||||
{
|
||||
return new StringHelper();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InternalEntityFactory $entityFactory
|
||||
*
|
||||
* @return ZipHelper
|
||||
*/
|
||||
private function createZipHelper($entityFactory)
|
||||
{
|
||||
return new ZipHelper($entityFactory);
|
||||
}
|
||||
}
|
@ -1,107 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Writer\ODS\Creator;
|
||||
|
||||
use OpenSpout\Common\Manager\OptionsManagerInterface;
|
||||
use OpenSpout\Writer\Common\Creator\InternalEntityFactory;
|
||||
use OpenSpout\Writer\Common\Creator\ManagerFactoryInterface;
|
||||
use OpenSpout\Writer\Common\Entity\Options;
|
||||
use OpenSpout\Writer\Common\Manager\SheetManager;
|
||||
use OpenSpout\Writer\Common\Manager\Style\StyleMerger;
|
||||
use OpenSpout\Writer\ODS\Manager\Style\StyleManager;
|
||||
use OpenSpout\Writer\ODS\Manager\Style\StyleRegistry;
|
||||
use OpenSpout\Writer\ODS\Manager\WorkbookManager;
|
||||
use OpenSpout\Writer\ODS\Manager\WorksheetManager;
|
||||
|
||||
/**
|
||||
* Factory for managers needed by the ODS Writer.
|
||||
*/
|
||||
class ManagerFactory implements ManagerFactoryInterface
|
||||
{
|
||||
/** @var InternalEntityFactory */
|
||||
protected $entityFactory;
|
||||
|
||||
/** @var HelperFactory */
|
||||
protected $helperFactory;
|
||||
|
||||
public function __construct(InternalEntityFactory $entityFactory, HelperFactory $helperFactory)
|
||||
{
|
||||
$this->entityFactory = $entityFactory;
|
||||
$this->helperFactory = $helperFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return WorkbookManager
|
||||
*/
|
||||
public function createWorkbookManager(OptionsManagerInterface $optionsManager)
|
||||
{
|
||||
$workbook = $this->entityFactory->createWorkbook();
|
||||
|
||||
$fileSystemHelper = $this->helperFactory->createSpecificFileSystemHelper($optionsManager, $this->entityFactory);
|
||||
$fileSystemHelper->createBaseFilesAndFolders();
|
||||
|
||||
$styleMerger = $this->createStyleMerger();
|
||||
$styleManager = $this->createStyleManager($optionsManager);
|
||||
$worksheetManager = $this->createWorksheetManager($styleManager, $styleMerger);
|
||||
|
||||
return new WorkbookManager(
|
||||
$workbook,
|
||||
$optionsManager,
|
||||
$worksheetManager,
|
||||
$styleManager,
|
||||
$styleMerger,
|
||||
$fileSystemHelper,
|
||||
$this->entityFactory,
|
||||
$this
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SheetManager
|
||||
*/
|
||||
public function createSheetManager()
|
||||
{
|
||||
$stringHelper = $this->helperFactory->createStringHelper();
|
||||
|
||||
return new SheetManager($stringHelper);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return WorksheetManager
|
||||
*/
|
||||
private function createWorksheetManager(StyleManager $styleManager, StyleMerger $styleMerger)
|
||||
{
|
||||
$stringsEscaper = $this->helperFactory->createStringsEscaper();
|
||||
$stringsHelper = $this->helperFactory->createStringHelper();
|
||||
|
||||
return new WorksheetManager($styleManager, $styleMerger, $stringsEscaper, $stringsHelper);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return StyleManager
|
||||
*/
|
||||
private function createStyleManager(OptionsManagerInterface $optionsManager)
|
||||
{
|
||||
$styleRegistry = $this->createStyleRegistry($optionsManager);
|
||||
|
||||
return new StyleManager($styleRegistry, $optionsManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return StyleRegistry
|
||||
*/
|
||||
private function createStyleRegistry(OptionsManagerInterface $optionsManager)
|
||||
{
|
||||
$defaultRowStyle = $optionsManager->getOption(Options::DEFAULT_ROW_STYLE);
|
||||
|
||||
return new StyleRegistry($defaultRowStyle);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return StyleMerger
|
||||
*/
|
||||
private function createStyleMerger()
|
||||
{
|
||||
return new StyleMerger();
|
||||
}
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Writer\ODS\Manager;
|
||||
|
||||
use OpenSpout\Common\Manager\OptionsManagerAbstract;
|
||||
use OpenSpout\Writer\Common\Creator\Style\StyleBuilder;
|
||||
use OpenSpout\Writer\Common\Entity\Options;
|
||||
|
||||
/**
|
||||
* ODS Writer options manager.
|
||||
*/
|
||||
class OptionsManager extends OptionsManagerAbstract
|
||||
{
|
||||
/** @var StyleBuilder Style builder */
|
||||
protected $styleBuilder;
|
||||
|
||||
/**
|
||||
* OptionsManager constructor.
|
||||
*/
|
||||
public function __construct(StyleBuilder $styleBuilder)
|
||||
{
|
||||
$this->styleBuilder = $styleBuilder;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getSupportedOptions()
|
||||
{
|
||||
return [
|
||||
Options::TEMP_FOLDER,
|
||||
Options::DEFAULT_ROW_STYLE,
|
||||
Options::SHOULD_CREATE_NEW_SHEETS_AUTOMATICALLY,
|
||||
Options::DEFAULT_COLUMN_WIDTH,
|
||||
Options::DEFAULT_ROW_HEIGHT,
|
||||
Options::COLUMN_WIDTHS,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setDefaultOptions()
|
||||
{
|
||||
$this->setOption(Options::TEMP_FOLDER, sys_get_temp_dir());
|
||||
$this->setOption(Options::DEFAULT_ROW_STYLE, $this->styleBuilder->build());
|
||||
$this->setOption(Options::SHOULD_CREATE_NEW_SHEETS_AUTOMATICALLY, true);
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Writer\ODS;
|
||||
|
||||
use OpenSpout\Writer\Common\Entity\Options;
|
||||
use OpenSpout\Writer\WriterMultiSheetsAbstract;
|
||||
|
||||
/**
|
||||
* This class provides base support to write data to ODS files.
|
||||
*/
|
||||
class Writer extends WriterMultiSheetsAbstract
|
||||
{
|
||||
/** @var string Content-Type value for the header */
|
||||
protected static $headerContentType = 'application/vnd.oasis.opendocument.spreadsheet';
|
||||
|
||||
/**
|
||||
* Sets a custom temporary folder for creating intermediate files/folders.
|
||||
* This must be set before opening the writer.
|
||||
*
|
||||
* @param string $tempFolder Temporary folder where the files to create the ODS will be stored
|
||||
*
|
||||
* @throws \OpenSpout\Writer\Exception\WriterAlreadyOpenedException If the writer was already opened
|
||||
*
|
||||
* @return Writer
|
||||
*/
|
||||
public function setTempFolder($tempFolder)
|
||||
{
|
||||
$this->throwIfWriterAlreadyOpened('Writer must be configured before opening it.');
|
||||
|
||||
$this->optionsManager->setOption(Options::TEMP_FOLDER, $tempFolder);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
@ -1,270 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Writer;
|
||||
|
||||
use OpenSpout\Common\Creator\HelperFactory;
|
||||
use OpenSpout\Common\Entity\Row;
|
||||
use OpenSpout\Common\Entity\Style\Style;
|
||||
use OpenSpout\Common\Exception\InvalidArgumentException;
|
||||
use OpenSpout\Common\Exception\IOException;
|
||||
use OpenSpout\Common\Exception\SpoutException;
|
||||
use OpenSpout\Common\Helper\GlobalFunctionsHelper;
|
||||
use OpenSpout\Common\Manager\OptionsManagerInterface;
|
||||
use OpenSpout\Writer\Common\Entity\Options;
|
||||
use OpenSpout\Writer\Exception\WriterAlreadyOpenedException;
|
||||
use OpenSpout\Writer\Exception\WriterNotOpenedException;
|
||||
|
||||
abstract class WriterAbstract implements WriterInterface
|
||||
{
|
||||
/** @var string Path to the output file */
|
||||
protected $outputFilePath;
|
||||
|
||||
/** @var resource Pointer to the file/stream we will write to */
|
||||
protected $filePointer;
|
||||
|
||||
/** @var bool faut-il garder ouvert le flux quand close() est appelé? */
|
||||
protected $dontCloseFilePointer = false;
|
||||
|
||||
/** @var bool Indicates whether the writer has been opened or not */
|
||||
protected $isWriterOpened = false;
|
||||
|
||||
/** @var GlobalFunctionsHelper Helper to work with global functions */
|
||||
protected $globalFunctionsHelper;
|
||||
|
||||
/** @var HelperFactory */
|
||||
protected $helperFactory;
|
||||
|
||||
/** @var OptionsManagerInterface Writer options manager */
|
||||
protected $optionsManager;
|
||||
|
||||
/** @var string Content-Type value for the header - to be defined by child class */
|
||||
protected static $headerContentType;
|
||||
|
||||
public function __construct(
|
||||
OptionsManagerInterface $optionsManager,
|
||||
GlobalFunctionsHelper $globalFunctionsHelper,
|
||||
HelperFactory $helperFactory
|
||||
) {
|
||||
$this->optionsManager = $optionsManager;
|
||||
$this->globalFunctionsHelper = $globalFunctionsHelper;
|
||||
$this->helperFactory = $helperFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setDefaultRowStyle(Style $defaultStyle)
|
||||
{
|
||||
$this->optionsManager->setOption(Options::DEFAULT_ROW_STYLE, $defaultStyle);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function writeToStream($filePointer)
|
||||
{
|
||||
$this->outputFilePath = null;
|
||||
|
||||
$this->filePointer = $filePointer;
|
||||
$this->dontCloseFilePointer = true;
|
||||
$this->throwIfFilePointerIsNotAvailable();
|
||||
|
||||
$this->openWriter();
|
||||
$this->isWriterOpened = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function openToFile($outputFilePath)
|
||||
{
|
||||
$this->outputFilePath = $outputFilePath;
|
||||
|
||||
$this->filePointer = $this->globalFunctionsHelper->fopen($this->outputFilePath, 'w');
|
||||
$this->throwIfFilePointerIsNotAvailable();
|
||||
|
||||
$this->openWriter();
|
||||
$this->isWriterOpened = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function openToBrowser($outputFileName)
|
||||
{
|
||||
$this->outputFilePath = $this->globalFunctionsHelper->basename($outputFileName);
|
||||
|
||||
$this->filePointer = $this->globalFunctionsHelper->fopen('php://output', 'w');
|
||||
$this->throwIfFilePointerIsNotAvailable();
|
||||
|
||||
// Clear any previous output (otherwise the generated file will be corrupted)
|
||||
// @see https://github.com/box/spout/issues/241
|
||||
$this->globalFunctionsHelper->ob_end_clean();
|
||||
|
||||
/*
|
||||
* Set headers
|
||||
*
|
||||
* For newer browsers such as Firefox, Chrome, Opera, Safari, etc., they all support and use `filename*`
|
||||
* specified by the new standard, even if they do not automatically decode filename; it does not matter;
|
||||
* and for older versions of Internet Explorer, they are not recognized `filename*`, will automatically
|
||||
* ignore it and use the old `filename` (the only minor flaw is that there must be an English suffix name).
|
||||
* In this way, the multi-browser multi-language compatibility problem is perfectly solved, which does not
|
||||
* require UA judgment and is more in line with the standard.
|
||||
*
|
||||
* @see https://github.com/box/spout/issues/745
|
||||
* @see https://tools.ietf.org/html/rfc6266
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition
|
||||
*/
|
||||
$this->globalFunctionsHelper->header('Content-Type: '.static::$headerContentType);
|
||||
$this->globalFunctionsHelper->header(
|
||||
'Content-Disposition: attachment; '.
|
||||
'filename="'.rawurlencode($this->outputFilePath).'"; '.
|
||||
'filename*=UTF-8\'\''.rawurlencode($this->outputFilePath)
|
||||
);
|
||||
|
||||
/*
|
||||
* When forcing the download of a file over SSL,IE8 and lower browsers fail
|
||||
* if the Cache-Control and Pragma headers are not set.
|
||||
*
|
||||
* @see http://support.microsoft.com/KB/323308
|
||||
* @see https://github.com/liuggio/ExcelBundle/issues/45
|
||||
*/
|
||||
$this->globalFunctionsHelper->header('Cache-Control: max-age=0');
|
||||
$this->globalFunctionsHelper->header('Pragma: public');
|
||||
|
||||
$this->openWriter();
|
||||
$this->isWriterOpened = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addRow(Row $row)
|
||||
{
|
||||
if ($this->isWriterOpened) {
|
||||
try {
|
||||
$this->addRowToWriter($row);
|
||||
} catch (SpoutException $e) {
|
||||
// if an exception occurs while writing data,
|
||||
// close the writer and remove all files created so far.
|
||||
$this->closeAndAttemptToCleanupAllFiles();
|
||||
|
||||
// re-throw the exception to alert developers of the error
|
||||
throw $e;
|
||||
}
|
||||
} else {
|
||||
throw new WriterNotOpenedException('The writer needs to be opened before adding row.');
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addRows(array $rows)
|
||||
{
|
||||
foreach ($rows as $row) {
|
||||
if (!$row instanceof Row) {
|
||||
$this->closeAndAttemptToCleanupAllFiles();
|
||||
|
||||
throw new InvalidArgumentException('The input should be an array of Row');
|
||||
}
|
||||
|
||||
$this->addRow($row);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
if (!$this->isWriterOpened) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->closeWriter();
|
||||
|
||||
if (!$this->dontCloseFilePointer && \is_resource($this->filePointer)) {
|
||||
$this->globalFunctionsHelper->fclose($this->filePointer);
|
||||
}
|
||||
|
||||
$this->isWriterOpened = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the streamer and makes it ready to accept data.
|
||||
*
|
||||
* @throws IOException If the writer cannot be opened
|
||||
*/
|
||||
abstract protected function openWriter();
|
||||
|
||||
/**
|
||||
* Adds a row to the currently opened writer.
|
||||
*
|
||||
* @param Row $row The row containing cells and styles
|
||||
*
|
||||
* @throws WriterNotOpenedException If the workbook is not created yet
|
||||
* @throws IOException If unable to write data
|
||||
*/
|
||||
abstract protected function addRowToWriter(Row $row);
|
||||
|
||||
/**
|
||||
* Closes the streamer, preventing any additional writing.
|
||||
*/
|
||||
abstract protected function closeWriter();
|
||||
|
||||
/**
|
||||
* Checks if the pointer to the file/stream to write to is available.
|
||||
* Will throw an exception if not available.
|
||||
*
|
||||
* @throws IOException If the pointer is not available
|
||||
*/
|
||||
protected function throwIfFilePointerIsNotAvailable()
|
||||
{
|
||||
if (!\is_resource($this->filePointer)) {
|
||||
throw new IOException('File pointer has not be opened');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the writer has already been opened, since some actions must be done before it gets opened.
|
||||
* Throws an exception if already opened.
|
||||
*
|
||||
* @param string $message Error message
|
||||
*
|
||||
* @throws WriterAlreadyOpenedException if the writer was already opened and must not be
|
||||
*/
|
||||
protected function throwIfWriterAlreadyOpened($message)
|
||||
{
|
||||
if ($this->isWriterOpened) {
|
||||
throw new WriterAlreadyOpenedException($message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the writer and attempts to cleanup all files that were
|
||||
* created during the writing process (temp files & final file).
|
||||
*/
|
||||
private function closeAndAttemptToCleanupAllFiles()
|
||||
{
|
||||
// close the writer, which should remove all temp files
|
||||
$this->close();
|
||||
|
||||
// remove output file if it was created
|
||||
if ($this->globalFunctionsHelper->file_exists($this->outputFilePath)) {
|
||||
$outputFolderPath = \dirname($this->outputFilePath);
|
||||
$fileSystemHelper = $this->helperFactory->createFileSystemHelper($outputFolderPath);
|
||||
$fileSystemHelper->deleteFile($this->outputFilePath);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,77 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Writer;
|
||||
|
||||
use OpenSpout\Common\Entity\Row;
|
||||
use OpenSpout\Common\Entity\Style\Style;
|
||||
|
||||
/**
|
||||
* Interface WriterInterface.
|
||||
*/
|
||||
interface WriterInterface
|
||||
{
|
||||
/**
|
||||
* Initializes the writer and opens it to accept data.
|
||||
* By using this method, the data will be written to a file.
|
||||
*
|
||||
* @param string $outputFilePath Path of the output file that will contain the data
|
||||
*
|
||||
* @throws \OpenSpout\Common\Exception\IOException If the writer cannot be opened or if the given path is not writable
|
||||
*
|
||||
* @return WriterInterface
|
||||
*/
|
||||
public function openToFile($outputFilePath);
|
||||
|
||||
/**
|
||||
* Initializes the writer and opens it to accept data.
|
||||
* By using this method, the data will be outputted directly to the browser.
|
||||
*
|
||||
* @param string $outputFileName Name of the output file that will contain the data. If a path is passed in, only the file name will be kept
|
||||
*
|
||||
* @throws \OpenSpout\Common\Exception\IOException If the writer cannot be opened
|
||||
*
|
||||
* @return WriterInterface
|
||||
*/
|
||||
public function openToBrowser($outputFileName);
|
||||
|
||||
/**
|
||||
* Sets the default styles for all rows added with "addRow".
|
||||
* Overriding the default style instead of using "addRowWithStyle" improves performance by 20%.
|
||||
*
|
||||
* @see https://github.com/box/spout/issues/272
|
||||
*
|
||||
* @return WriterInterface
|
||||
*/
|
||||
public function setDefaultRowStyle(Style $defaultStyle);
|
||||
|
||||
/**
|
||||
* Appends a row to the end of the stream.
|
||||
*
|
||||
* @param Row $row The row to be appended to the stream
|
||||
*
|
||||
* @throws \OpenSpout\Writer\Exception\WriterNotOpenedException If the writer has not been opened yet
|
||||
* @throws \OpenSpout\Common\Exception\IOException If unable to write data
|
||||
*
|
||||
* @return WriterInterface
|
||||
*/
|
||||
public function addRow(Row $row);
|
||||
|
||||
/**
|
||||
* Appends the rows to the end of the stream.
|
||||
*
|
||||
* @param Row[] $rows The rows to be appended to the stream
|
||||
*
|
||||
* @throws \OpenSpout\Common\Exception\InvalidArgumentException If the input param is not valid
|
||||
* @throws \OpenSpout\Writer\Exception\WriterNotOpenedException If the writer has not been opened yet
|
||||
* @throws \OpenSpout\Common\Exception\IOException If unable to write data
|
||||
*
|
||||
* @return WriterInterface
|
||||
*/
|
||||
public function addRows(array $rows);
|
||||
|
||||
/**
|
||||
* Closes the writer. This will close the streamer as well, preventing new data
|
||||
* to be written to the file.
|
||||
*/
|
||||
public function close();
|
||||
}
|
@ -1,216 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Writer;
|
||||
|
||||
use OpenSpout\Common\Creator\HelperFactory;
|
||||
use OpenSpout\Common\Entity\Row;
|
||||
use OpenSpout\Common\Exception\IOException;
|
||||
use OpenSpout\Common\Helper\GlobalFunctionsHelper;
|
||||
use OpenSpout\Common\Manager\OptionsManagerInterface;
|
||||
use OpenSpout\Writer\Common\Creator\ManagerFactoryInterface;
|
||||
use OpenSpout\Writer\Common\Entity\Options;
|
||||
use OpenSpout\Writer\Common\Entity\Sheet;
|
||||
use OpenSpout\Writer\Common\Manager\WorkbookManagerInterface;
|
||||
use OpenSpout\Writer\Exception\SheetNotFoundException;
|
||||
use OpenSpout\Writer\Exception\WriterAlreadyOpenedException;
|
||||
use OpenSpout\Writer\Exception\WriterNotOpenedException;
|
||||
|
||||
abstract class WriterMultiSheetsAbstract extends WriterAbstract
|
||||
{
|
||||
/** @var ManagerFactoryInterface */
|
||||
private $managerFactory;
|
||||
|
||||
/** @var null|WorkbookManagerInterface */
|
||||
private $workbookManager;
|
||||
|
||||
public function __construct(
|
||||
OptionsManagerInterface $optionsManager,
|
||||
GlobalFunctionsHelper $globalFunctionsHelper,
|
||||
HelperFactory $helperFactory,
|
||||
ManagerFactoryInterface $managerFactory
|
||||
) {
|
||||
parent::__construct($optionsManager, $globalFunctionsHelper, $helperFactory);
|
||||
$this->managerFactory = $managerFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether new sheets should be automatically created when the max rows limit per sheet is reached.
|
||||
* This must be set before opening the writer.
|
||||
*
|
||||
* @param bool $shouldCreateNewSheetsAutomatically Whether new sheets should be automatically created when the max rows limit per sheet is reached
|
||||
*
|
||||
* @throws WriterAlreadyOpenedException If the writer was already opened
|
||||
*
|
||||
* @return WriterMultiSheetsAbstract
|
||||
*/
|
||||
public function setShouldCreateNewSheetsAutomatically($shouldCreateNewSheetsAutomatically)
|
||||
{
|
||||
$this->throwIfWriterAlreadyOpened('Writer must be configured before opening it.');
|
||||
|
||||
$this->optionsManager->setOption(
|
||||
Options::SHOULD_CREATE_NEW_SHEETS_AUTOMATICALLY,
|
||||
$shouldCreateNewSheetsAutomatically
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all the workbook's sheets.
|
||||
*
|
||||
* @throws WriterNotOpenedException If the writer has not been opened yet
|
||||
*
|
||||
* @return Sheet[] All the workbook's sheets
|
||||
*/
|
||||
public function getSheets()
|
||||
{
|
||||
$this->throwIfWorkbookIsNotAvailable();
|
||||
|
||||
$externalSheets = [];
|
||||
$worksheets = $this->workbookManager->getWorksheets();
|
||||
|
||||
foreach ($worksheets as $worksheet) {
|
||||
$externalSheets[] = $worksheet->getExternalSheet();
|
||||
}
|
||||
|
||||
return $externalSheets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new sheet and make it the current sheet. The data will now be written to this sheet.
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws WriterNotOpenedException If the writer has not been opened yet
|
||||
*
|
||||
* @return Sheet The created sheet
|
||||
*/
|
||||
public function addNewSheetAndMakeItCurrent()
|
||||
{
|
||||
$this->throwIfWorkbookIsNotAvailable();
|
||||
$worksheet = $this->workbookManager->addNewSheetAndMakeItCurrent();
|
||||
|
||||
return $worksheet->getExternalSheet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current sheet.
|
||||
*
|
||||
* @throws WriterNotOpenedException If the writer has not been opened yet
|
||||
*
|
||||
* @return Sheet The current sheet
|
||||
*/
|
||||
public function getCurrentSheet()
|
||||
{
|
||||
$this->throwIfWorkbookIsNotAvailable();
|
||||
|
||||
return $this->workbookManager->getCurrentWorksheet()->getExternalSheet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the given sheet as the current one. New data will be written to this sheet.
|
||||
* The writing will resume where it stopped (i.e. data won't be truncated).
|
||||
*
|
||||
* @param Sheet $sheet The sheet to set as current
|
||||
*
|
||||
* @throws SheetNotFoundException If the given sheet does not exist in the workbook
|
||||
* @throws WriterNotOpenedException If the writer has not been opened yet
|
||||
*/
|
||||
public function setCurrentSheet($sheet)
|
||||
{
|
||||
$this->throwIfWorkbookIsNotAvailable();
|
||||
$this->workbookManager->setCurrentSheet($sheet);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws WriterAlreadyOpenedException
|
||||
*/
|
||||
public function setDefaultColumnWidth(float $width)
|
||||
{
|
||||
$this->throwIfWriterAlreadyOpened('Writer must be configured before opening it.');
|
||||
$this->optionsManager->setOption(
|
||||
Options::DEFAULT_COLUMN_WIDTH,
|
||||
$width
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws WriterAlreadyOpenedException
|
||||
*/
|
||||
public function setDefaultRowHeight(float $height)
|
||||
{
|
||||
$this->throwIfWriterAlreadyOpened('Writer must be configured before opening it.');
|
||||
$this->optionsManager->setOption(
|
||||
Options::DEFAULT_ROW_HEIGHT,
|
||||
$height
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null|float $width
|
||||
* @param int ...$columns One or more columns with this width
|
||||
*
|
||||
* @throws WriterNotOpenedException
|
||||
*/
|
||||
public function setColumnWidth($width, ...$columns)
|
||||
{
|
||||
$this->throwIfWorkbookIsNotAvailable();
|
||||
$this->workbookManager->setColumnWidth($width, ...$columns);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param float $width The width to set
|
||||
* @param int $start First column index of the range
|
||||
* @param int $end Last column index of the range
|
||||
*
|
||||
* @throws WriterNotOpenedException
|
||||
*/
|
||||
public function setColumnWidthForRange(float $width, int $start, int $end)
|
||||
{
|
||||
$this->throwIfWorkbookIsNotAvailable();
|
||||
$this->workbookManager->setColumnWidthForRange($width, $start, $end);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function openWriter()
|
||||
{
|
||||
if (null === $this->workbookManager) {
|
||||
$this->workbookManager = $this->managerFactory->createWorkbookManager($this->optionsManager);
|
||||
$this->workbookManager->addNewSheetAndMakeItCurrent();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the workbook has been created. Throws an exception if not created yet.
|
||||
*
|
||||
* @throws WriterNotOpenedException If the workbook is not created yet
|
||||
*/
|
||||
protected function throwIfWorkbookIsNotAvailable()
|
||||
{
|
||||
if (!$this->workbookManager->getWorkbook()) {
|
||||
throw new WriterNotOpenedException('The writer must be opened before performing this action.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @throws Exception\WriterException
|
||||
*/
|
||||
protected function addRowToWriter(Row $row)
|
||||
{
|
||||
$this->throwIfWorkbookIsNotAvailable();
|
||||
$this->workbookManager->addRowToCurrentWorksheet($row);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function closeWriter()
|
||||
{
|
||||
if (null !== $this->workbookManager) {
|
||||
$this->workbookManager->close($this->filePointer);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Writer\XLSX\Creator;
|
||||
|
||||
use OpenSpout\Common\Helper\Escaper;
|
||||
use OpenSpout\Common\Helper\StringHelper;
|
||||
use OpenSpout\Common\Manager\OptionsManagerInterface;
|
||||
use OpenSpout\Writer\Common\Creator\InternalEntityFactory;
|
||||
use OpenSpout\Writer\Common\Entity\Options;
|
||||
use OpenSpout\Writer\Common\Helper\ZipHelper;
|
||||
use OpenSpout\Writer\XLSX\Helper\FileSystemHelper;
|
||||
|
||||
/**
|
||||
* Factory for helpers needed by the XLSX Writer.
|
||||
*/
|
||||
class HelperFactory extends \OpenSpout\Common\Creator\HelperFactory
|
||||
{
|
||||
/**
|
||||
* @return FileSystemHelper
|
||||
*/
|
||||
public function createSpecificFileSystemHelper(OptionsManagerInterface $optionsManager, InternalEntityFactory $entityFactory)
|
||||
{
|
||||
$tempFolder = $optionsManager->getOption(Options::TEMP_FOLDER);
|
||||
$zipHelper = $this->createZipHelper($entityFactory);
|
||||
$escaper = $this->createStringsEscaper();
|
||||
|
||||
return new FileSystemHelper($tempFolder, $zipHelper, $escaper);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Escaper\XLSX
|
||||
*/
|
||||
public function createStringsEscaper()
|
||||
{
|
||||
return new Escaper\XLSX();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return StringHelper
|
||||
*/
|
||||
public function createStringHelper()
|
||||
{
|
||||
return new StringHelper();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ZipHelper
|
||||
*/
|
||||
private function createZipHelper(InternalEntityFactory $entityFactory)
|
||||
{
|
||||
return new ZipHelper($entityFactory);
|
||||
}
|
||||
}
|
@ -1,145 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Writer\XLSX\Creator;
|
||||
|
||||
use OpenSpout\Common\Manager\OptionsManagerInterface;
|
||||
use OpenSpout\Writer\Common\Creator\InternalEntityFactory;
|
||||
use OpenSpout\Writer\Common\Creator\ManagerFactoryInterface;
|
||||
use OpenSpout\Writer\Common\Entity\Options;
|
||||
use OpenSpout\Writer\Common\Manager\RowManager;
|
||||
use OpenSpout\Writer\Common\Manager\SheetManager;
|
||||
use OpenSpout\Writer\Common\Manager\Style\StyleMerger;
|
||||
use OpenSpout\Writer\XLSX\Manager\SharedStringsManager;
|
||||
use OpenSpout\Writer\XLSX\Manager\Style\StyleManager;
|
||||
use OpenSpout\Writer\XLSX\Manager\Style\StyleRegistry;
|
||||
use OpenSpout\Writer\XLSX\Manager\WorkbookManager;
|
||||
use OpenSpout\Writer\XLSX\Manager\WorksheetManager;
|
||||
|
||||
/**
|
||||
* Factory for managers needed by the XLSX Writer.
|
||||
*/
|
||||
class ManagerFactory implements ManagerFactoryInterface
|
||||
{
|
||||
/** @var InternalEntityFactory */
|
||||
protected $entityFactory;
|
||||
|
||||
/** @var HelperFactory */
|
||||
protected $helperFactory;
|
||||
|
||||
public function __construct(InternalEntityFactory $entityFactory, HelperFactory $helperFactory)
|
||||
{
|
||||
$this->entityFactory = $entityFactory;
|
||||
$this->helperFactory = $helperFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return WorkbookManager
|
||||
*/
|
||||
public function createWorkbookManager(OptionsManagerInterface $optionsManager)
|
||||
{
|
||||
$workbook = $this->entityFactory->createWorkbook();
|
||||
|
||||
$fileSystemHelper = $this->helperFactory->createSpecificFileSystemHelper($optionsManager, $this->entityFactory);
|
||||
$fileSystemHelper->createBaseFilesAndFolders();
|
||||
|
||||
$xlFolder = $fileSystemHelper->getXlFolder();
|
||||
$sharedStringsManager = $this->createSharedStringsManager($xlFolder);
|
||||
|
||||
$styleMerger = $this->createStyleMerger();
|
||||
$styleManager = $this->createStyleManager($optionsManager);
|
||||
$worksheetManager = $this->createWorksheetManager($optionsManager, $styleManager, $styleMerger, $sharedStringsManager);
|
||||
|
||||
return new WorkbookManager(
|
||||
$workbook,
|
||||
$optionsManager,
|
||||
$worksheetManager,
|
||||
$styleManager,
|
||||
$styleMerger,
|
||||
$fileSystemHelper,
|
||||
$this->entityFactory,
|
||||
$this
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SheetManager
|
||||
*/
|
||||
public function createSheetManager()
|
||||
{
|
||||
$stringHelper = $this->helperFactory->createStringHelper();
|
||||
|
||||
return new SheetManager($stringHelper);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return RowManager
|
||||
*/
|
||||
public function createRowManager()
|
||||
{
|
||||
return new RowManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return WorksheetManager
|
||||
*/
|
||||
private function createWorksheetManager(
|
||||
OptionsManagerInterface $optionsManager,
|
||||
StyleManager $styleManager,
|
||||
StyleMerger $styleMerger,
|
||||
SharedStringsManager $sharedStringsManager
|
||||
) {
|
||||
$rowManager = $this->createRowManager();
|
||||
$stringsEscaper = $this->helperFactory->createStringsEscaper();
|
||||
$stringsHelper = $this->helperFactory->createStringHelper();
|
||||
|
||||
return new WorksheetManager(
|
||||
$optionsManager,
|
||||
$rowManager,
|
||||
$styleManager,
|
||||
$styleMerger,
|
||||
$sharedStringsManager,
|
||||
$stringsEscaper,
|
||||
$stringsHelper
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return StyleManager
|
||||
*/
|
||||
private function createStyleManager(OptionsManagerInterface $optionsManager)
|
||||
{
|
||||
$styleRegistry = $this->createStyleRegistry($optionsManager);
|
||||
|
||||
return new StyleManager($styleRegistry);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return StyleRegistry
|
||||
*/
|
||||
private function createStyleRegistry(OptionsManagerInterface $optionsManager)
|
||||
{
|
||||
$defaultRowStyle = $optionsManager->getOption(Options::DEFAULT_ROW_STYLE);
|
||||
|
||||
return new StyleRegistry($defaultRowStyle);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return StyleMerger
|
||||
*/
|
||||
private function createStyleMerger()
|
||||
{
|
||||
return new StyleMerger();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $xlFolder Path to the "xl" folder
|
||||
*
|
||||
* @return SharedStringsManager
|
||||
*/
|
||||
private function createSharedStringsManager($xlFolder)
|
||||
{
|
||||
$stringEscaper = $this->helperFactory->createStringsEscaper();
|
||||
|
||||
return new SharedStringsManager($xlFolder, $stringEscaper);
|
||||
}
|
||||
}
|
@ -1,403 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Writer\XLSX\Helper;
|
||||
|
||||
use OpenSpout\Writer\Common\Entity\Worksheet;
|
||||
use OpenSpout\Writer\Common\Helper\FileSystemWithRootFolderHelperInterface;
|
||||
use OpenSpout\Writer\Common\Helper\ZipHelper;
|
||||
use OpenSpout\Writer\XLSX\Manager\Style\StyleManager;
|
||||
|
||||
/**
|
||||
* This class provides helper functions to help with the file system operations
|
||||
* like files/folders creation & deletion for XLSX files.
|
||||
*/
|
||||
class FileSystemHelper extends \OpenSpout\Common\Helper\FileSystemHelper implements FileSystemWithRootFolderHelperInterface
|
||||
{
|
||||
public const APP_NAME = 'Spout';
|
||||
|
||||
public const RELS_FOLDER_NAME = '_rels';
|
||||
public const DOC_PROPS_FOLDER_NAME = 'docProps';
|
||||
public const XL_FOLDER_NAME = 'xl';
|
||||
public const WORKSHEETS_FOLDER_NAME = 'worksheets';
|
||||
|
||||
public const RELS_FILE_NAME = '.rels';
|
||||
public const APP_XML_FILE_NAME = 'app.xml';
|
||||
public const CORE_XML_FILE_NAME = 'core.xml';
|
||||
public const CONTENT_TYPES_XML_FILE_NAME = '[Content_Types].xml';
|
||||
public const WORKBOOK_XML_FILE_NAME = 'workbook.xml';
|
||||
public const WORKBOOK_RELS_XML_FILE_NAME = 'workbook.xml.rels';
|
||||
public const STYLES_XML_FILE_NAME = 'styles.xml';
|
||||
|
||||
/** @var ZipHelper Helper to perform tasks with Zip archive */
|
||||
private $zipHelper;
|
||||
|
||||
/** @var \OpenSpout\Common\Helper\Escaper\XLSX Used to escape XML data */
|
||||
private $escaper;
|
||||
|
||||
/** @var string Path to the root folder inside the temp folder where the files to create the XLSX will be stored */
|
||||
private $rootFolder;
|
||||
|
||||
/** @var string Path to the "_rels" folder inside the root folder */
|
||||
private $relsFolder;
|
||||
|
||||
/** @var string Path to the "docProps" folder inside the root folder */
|
||||
private $docPropsFolder;
|
||||
|
||||
/** @var string Path to the "xl" folder inside the root folder */
|
||||
private $xlFolder;
|
||||
|
||||
/** @var string Path to the "_rels" folder inside the "xl" folder */
|
||||
private $xlRelsFolder;
|
||||
|
||||
/** @var string Path to the "worksheets" folder inside the "xl" folder */
|
||||
private $xlWorksheetsFolder;
|
||||
|
||||
/**
|
||||
* @param string $baseFolderPath The path of the base folder where all the I/O can occur
|
||||
* @param ZipHelper $zipHelper Helper to perform tasks with Zip archive
|
||||
* @param \OpenSpout\Common\Helper\Escaper\XLSX $escaper Used to escape XML data
|
||||
*/
|
||||
public function __construct($baseFolderPath, $zipHelper, $escaper)
|
||||
{
|
||||
parent::__construct($baseFolderPath);
|
||||
$this->zipHelper = $zipHelper;
|
||||
$this->escaper = $escaper;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getRootFolder()
|
||||
{
|
||||
return $this->rootFolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getXlFolder()
|
||||
{
|
||||
return $this->xlFolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getXlWorksheetsFolder()
|
||||
{
|
||||
return $this->xlWorksheetsFolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates all the folders needed to create a XLSX file, as well as the files that won't change.
|
||||
*
|
||||
* @throws \OpenSpout\Common\Exception\IOException If unable to create at least one of the base folders
|
||||
*/
|
||||
public function createBaseFilesAndFolders()
|
||||
{
|
||||
$this
|
||||
->createRootFolder()
|
||||
->createRelsFolderAndFile()
|
||||
->createDocPropsFolderAndFiles()
|
||||
->createXlFolderAndSubFolders()
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the "[Content_Types].xml" file under the root folder.
|
||||
*
|
||||
* @param Worksheet[] $worksheets
|
||||
*
|
||||
* @return FileSystemHelper
|
||||
*/
|
||||
public function createContentTypesFile($worksheets)
|
||||
{
|
||||
$contentTypesXmlFileContents = <<<'EOD'
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
|
||||
<Default ContentType="application/xml" Extension="xml"/>
|
||||
<Default ContentType="application/vnd.openxmlformats-package.relationships+xml" Extension="rels"/>
|
||||
<Override ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml" PartName="/xl/workbook.xml"/>
|
||||
EOD;
|
||||
|
||||
/** @var Worksheet $worksheet */
|
||||
foreach ($worksheets as $worksheet) {
|
||||
$contentTypesXmlFileContents .= '<Override ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml" PartName="/xl/worksheets/sheet'.$worksheet->getId().'.xml"/>';
|
||||
}
|
||||
|
||||
$contentTypesXmlFileContents .= <<<'EOD'
|
||||
<Override ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml" PartName="/xl/styles.xml"/>
|
||||
<Override ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml" PartName="/xl/sharedStrings.xml"/>
|
||||
<Override ContentType="application/vnd.openxmlformats-package.core-properties+xml" PartName="/docProps/core.xml"/>
|
||||
<Override ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml" PartName="/docProps/app.xml"/>
|
||||
</Types>
|
||||
EOD;
|
||||
|
||||
$this->createFileWithContents($this->rootFolder, self::CONTENT_TYPES_XML_FILE_NAME, $contentTypesXmlFileContents);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the "workbook.xml" file under the "xl" folder.
|
||||
*
|
||||
* @param Worksheet[] $worksheets
|
||||
*
|
||||
* @return FileSystemHelper
|
||||
*/
|
||||
public function createWorkbookFile($worksheets)
|
||||
{
|
||||
$workbookXmlFileContents = <<<'EOD'
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
|
||||
<sheets>
|
||||
EOD;
|
||||
|
||||
/** @var Worksheet $worksheet */
|
||||
foreach ($worksheets as $worksheet) {
|
||||
$worksheetName = $worksheet->getExternalSheet()->getName();
|
||||
$worksheetVisibility = $worksheet->getExternalSheet()->isVisible() ? 'visible' : 'hidden';
|
||||
$worksheetId = $worksheet->getId();
|
||||
$workbookXmlFileContents .= '<sheet name="'.$this->escaper->escape($worksheetName).'" sheetId="'.$worksheetId.'" r:id="rIdSheet'.$worksheetId.'" state="'.$worksheetVisibility.'"/>';
|
||||
}
|
||||
|
||||
$workbookXmlFileContents .= <<<'EOD'
|
||||
</sheets>
|
||||
</workbook>
|
||||
EOD;
|
||||
|
||||
$this->createFileWithContents($this->xlFolder, self::WORKBOOK_XML_FILE_NAME, $workbookXmlFileContents);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the "workbook.xml.res" file under the "xl/_res" folder.
|
||||
*
|
||||
* @param Worksheet[] $worksheets
|
||||
*
|
||||
* @return FileSystemHelper
|
||||
*/
|
||||
public function createWorkbookRelsFile($worksheets)
|
||||
{
|
||||
$workbookRelsXmlFileContents = <<<'EOD'
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
||||
<Relationship Id="rIdStyles" Target="styles.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles"/>
|
||||
<Relationship Id="rIdSharedStrings" Target="sharedStrings.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings"/>
|
||||
EOD;
|
||||
|
||||
/** @var Worksheet $worksheet */
|
||||
foreach ($worksheets as $worksheet) {
|
||||
$worksheetId = $worksheet->getId();
|
||||
$workbookRelsXmlFileContents .= '<Relationship Id="rIdSheet'.$worksheetId.'" Target="worksheets/sheet'.$worksheetId.'.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet"/>';
|
||||
}
|
||||
|
||||
$workbookRelsXmlFileContents .= '</Relationships>';
|
||||
|
||||
$this->createFileWithContents($this->xlRelsFolder, self::WORKBOOK_RELS_XML_FILE_NAME, $workbookRelsXmlFileContents);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the "styles.xml" file under the "xl" folder.
|
||||
*
|
||||
* @param StyleManager $styleManager
|
||||
*
|
||||
* @return FileSystemHelper
|
||||
*/
|
||||
public function createStylesFile($styleManager)
|
||||
{
|
||||
$stylesXmlFileContents = $styleManager->getStylesXMLFileContent();
|
||||
$this->createFileWithContents($this->xlFolder, self::STYLES_XML_FILE_NAME, $stylesXmlFileContents);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Zips the root folder and streams the contents of the zip into the given stream.
|
||||
*
|
||||
* @param resource $streamPointer Pointer to the stream to copy the zip
|
||||
*/
|
||||
public function zipRootFolderAndCopyToStream($streamPointer)
|
||||
{
|
||||
$zip = $this->zipHelper->createZip($this->rootFolder);
|
||||
|
||||
$zipFilePath = $this->zipHelper->getZipFilePath($zip);
|
||||
|
||||
// In order to have the file's mime type detected properly, files need to be added
|
||||
// to the zip file in a particular order.
|
||||
// "[Content_Types].xml" then at least 2 files located in "xl" folder should be zipped first.
|
||||
$this->zipHelper->addFileToArchive($zip, $this->rootFolder, self::CONTENT_TYPES_XML_FILE_NAME);
|
||||
$this->zipHelper->addFileToArchive($zip, $this->rootFolder, self::XL_FOLDER_NAME.'/'.self::WORKBOOK_XML_FILE_NAME);
|
||||
$this->zipHelper->addFileToArchive($zip, $this->rootFolder, self::XL_FOLDER_NAME.'/'.self::STYLES_XML_FILE_NAME);
|
||||
|
||||
$this->zipHelper->addFolderToArchive($zip, $this->rootFolder, ZipHelper::EXISTING_FILES_SKIP);
|
||||
$this->zipHelper->closeArchiveAndCopyToStream($zip, $streamPointer);
|
||||
|
||||
// once the zip is copied, remove it
|
||||
$this->deleteFile($zipFilePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the folder that will be used as root.
|
||||
*
|
||||
* @throws \OpenSpout\Common\Exception\IOException If unable to create the folder
|
||||
*
|
||||
* @return FileSystemHelper
|
||||
*/
|
||||
private function createRootFolder()
|
||||
{
|
||||
$this->rootFolder = $this->createFolder($this->baseFolderRealPath, uniqid('xlsx', true));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the "_rels" folder under the root folder as well as the ".rels" file in it.
|
||||
*
|
||||
* @throws \OpenSpout\Common\Exception\IOException If unable to create the folder or the ".rels" file
|
||||
*
|
||||
* @return FileSystemHelper
|
||||
*/
|
||||
private function createRelsFolderAndFile()
|
||||
{
|
||||
$this->relsFolder = $this->createFolder($this->rootFolder, self::RELS_FOLDER_NAME);
|
||||
|
||||
$this->createRelsFile();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the ".rels" file under the "_rels" folder (under root).
|
||||
*
|
||||
* @throws \OpenSpout\Common\Exception\IOException If unable to create the file
|
||||
*
|
||||
* @return FileSystemHelper
|
||||
*/
|
||||
private function createRelsFile()
|
||||
{
|
||||
$relsFileContents = <<<'EOD'
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
||||
<Relationship Id="rIdWorkbook" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>
|
||||
<Relationship Id="rIdCore" Type="http://schemas.openxmlformats.org/officedocument/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/>
|
||||
<Relationship Id="rIdApp" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/>
|
||||
</Relationships>
|
||||
EOD;
|
||||
|
||||
$this->createFileWithContents($this->relsFolder, self::RELS_FILE_NAME, $relsFileContents);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the "docProps" folder under the root folder as well as the "app.xml" and "core.xml" files in it.
|
||||
*
|
||||
* @throws \OpenSpout\Common\Exception\IOException If unable to create the folder or one of the files
|
||||
*
|
||||
* @return FileSystemHelper
|
||||
*/
|
||||
private function createDocPropsFolderAndFiles()
|
||||
{
|
||||
$this->docPropsFolder = $this->createFolder($this->rootFolder, self::DOC_PROPS_FOLDER_NAME);
|
||||
|
||||
$this->createAppXmlFile();
|
||||
$this->createCoreXmlFile();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the "app.xml" file under the "docProps" folder.
|
||||
*
|
||||
* @throws \OpenSpout\Common\Exception\IOException If unable to create the file
|
||||
*
|
||||
* @return FileSystemHelper
|
||||
*/
|
||||
private function createAppXmlFile()
|
||||
{
|
||||
$appName = self::APP_NAME;
|
||||
$appXmlFileContents = <<<EOD
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties">
|
||||
<Application>{$appName}</Application>
|
||||
<TotalTime>0</TotalTime>
|
||||
</Properties>
|
||||
EOD;
|
||||
|
||||
$this->createFileWithContents($this->docPropsFolder, self::APP_XML_FILE_NAME, $appXmlFileContents);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the "core.xml" file under the "docProps" folder.
|
||||
*
|
||||
* @throws \OpenSpout\Common\Exception\IOException If unable to create the file
|
||||
*
|
||||
* @return FileSystemHelper
|
||||
*/
|
||||
private function createCoreXmlFile()
|
||||
{
|
||||
$createdDate = (new \DateTime())->format(\DateTime::W3C);
|
||||
$coreXmlFileContents = <<<EOD
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<dcterms:created xsi:type="dcterms:W3CDTF">{$createdDate}</dcterms:created>
|
||||
<dcterms:modified xsi:type="dcterms:W3CDTF">{$createdDate}</dcterms:modified>
|
||||
<cp:revision>0</cp:revision>
|
||||
</cp:coreProperties>
|
||||
EOD;
|
||||
|
||||
$this->createFileWithContents($this->docPropsFolder, self::CORE_XML_FILE_NAME, $coreXmlFileContents);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the "xl" folder under the root folder as well as its subfolders.
|
||||
*
|
||||
* @throws \OpenSpout\Common\Exception\IOException If unable to create at least one of the folders
|
||||
*
|
||||
* @return FileSystemHelper
|
||||
*/
|
||||
private function createXlFolderAndSubFolders()
|
||||
{
|
||||
$this->xlFolder = $this->createFolder($this->rootFolder, self::XL_FOLDER_NAME);
|
||||
$this->createXlRelsFolder();
|
||||
$this->createXlWorksheetsFolder();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the "_rels" folder under the "xl" folder.
|
||||
*
|
||||
* @throws \OpenSpout\Common\Exception\IOException If unable to create the folder
|
||||
*
|
||||
* @return FileSystemHelper
|
||||
*/
|
||||
private function createXlRelsFolder()
|
||||
{
|
||||
$this->xlRelsFolder = $this->createFolder($this->xlFolder, self::RELS_FOLDER_NAME);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the "worksheets" folder under the "xl" folder.
|
||||
*
|
||||
* @throws \OpenSpout\Common\Exception\IOException If unable to create the folder
|
||||
*
|
||||
* @return FileSystemHelper
|
||||
*/
|
||||
private function createXlWorksheetsFolder()
|
||||
{
|
||||
$this->xlWorksheetsFolder = $this->createFolder($this->xlFolder, self::WORKSHEETS_FOLDER_NAME);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Writer\XLSX\Manager;
|
||||
|
||||
use OpenSpout\Common\Manager\OptionsManagerAbstract;
|
||||
use OpenSpout\Writer\Common\Creator\Style\StyleBuilder;
|
||||
use OpenSpout\Writer\Common\Entity\Options;
|
||||
|
||||
/**
|
||||
* XLSX Writer options manager.
|
||||
*/
|
||||
class OptionsManager extends OptionsManagerAbstract
|
||||
{
|
||||
/** Default style font values */
|
||||
public const DEFAULT_FONT_SIZE = 12;
|
||||
public const DEFAULT_FONT_NAME = 'Calibri';
|
||||
|
||||
/** @var StyleBuilder Style builder */
|
||||
protected $styleBuilder;
|
||||
|
||||
/**
|
||||
* OptionsManager constructor.
|
||||
*/
|
||||
public function __construct(StyleBuilder $styleBuilder)
|
||||
{
|
||||
$this->styleBuilder = $styleBuilder;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getSupportedOptions()
|
||||
{
|
||||
return [
|
||||
Options::TEMP_FOLDER,
|
||||
Options::DEFAULT_ROW_STYLE,
|
||||
Options::SHOULD_CREATE_NEW_SHEETS_AUTOMATICALLY,
|
||||
Options::SHOULD_USE_INLINE_STRINGS,
|
||||
Options::DEFAULT_COLUMN_WIDTH,
|
||||
Options::DEFAULT_ROW_HEIGHT,
|
||||
Options::COLUMN_WIDTHS,
|
||||
Options::MERGE_CELLS,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setDefaultOptions()
|
||||
{
|
||||
$defaultRowStyle = $this->styleBuilder
|
||||
->setFontSize(self::DEFAULT_FONT_SIZE)
|
||||
->setFontName(self::DEFAULT_FONT_NAME)
|
||||
->build()
|
||||
;
|
||||
|
||||
$this->setOption(Options::TEMP_FOLDER, sys_get_temp_dir());
|
||||
$this->setOption(Options::DEFAULT_ROW_STYLE, $defaultRowStyle);
|
||||
$this->setOption(Options::SHOULD_CREATE_NEW_SHEETS_AUTOMATICALLY, true);
|
||||
$this->setOption(Options::SHOULD_USE_INLINE_STRINGS, true);
|
||||
$this->setOption(Options::MERGE_CELLS, []);
|
||||
}
|
||||
}
|
@ -1,74 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Writer\XLSX\Manager;
|
||||
|
||||
use OpenSpout\Writer\Common\Entity\Sheet;
|
||||
use OpenSpout\Writer\Common\Manager\WorkbookManagerAbstract;
|
||||
use OpenSpout\Writer\XLSX\Helper\FileSystemHelper;
|
||||
use OpenSpout\Writer\XLSX\Manager\Style\StyleManager;
|
||||
|
||||
/**
|
||||
* XLSX workbook manager, providing the interfaces to work with workbook.
|
||||
*/
|
||||
class WorkbookManager extends WorkbookManagerAbstract
|
||||
{
|
||||
/**
|
||||
* Maximum number of rows a XLSX sheet can contain.
|
||||
*
|
||||
* @see http://office.microsoft.com/en-us/excel-help/excel-specifications-and-limits-HP010073849.aspx
|
||||
*/
|
||||
protected static $maxRowsPerWorksheet = 1048576;
|
||||
|
||||
/** @var WorksheetManager Object used to manage worksheets */
|
||||
protected $worksheetManager;
|
||||
|
||||
/** @var StyleManager Manages styles */
|
||||
protected $styleManager;
|
||||
|
||||
/** @var FileSystemHelper Helper to perform file system operations */
|
||||
protected $fileSystemHelper;
|
||||
|
||||
/**
|
||||
* @return string The file path where the data for the given sheet will be stored
|
||||
*/
|
||||
public function getWorksheetFilePath(Sheet $sheet)
|
||||
{
|
||||
$worksheetFilesFolder = $this->fileSystemHelper->getXlWorksheetsFolder();
|
||||
|
||||
return $worksheetFilesFolder.'/'.strtolower($sheet->getName()).'.xml';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int Maximum number of rows/columns a sheet can contain
|
||||
*/
|
||||
protected function getMaxRowsPerWorksheet()
|
||||
{
|
||||
return self::$maxRowsPerWorksheet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes custom objects that are still opened.
|
||||
*/
|
||||
protected function closeRemainingObjects()
|
||||
{
|
||||
$this->worksheetManager->getSharedStringsManager()->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes all the necessary files to disk and zip them together to create the final file.
|
||||
*
|
||||
* @param resource $finalFilePointer Pointer to the spreadsheet that will be created
|
||||
*/
|
||||
protected function writeAllFilesToDiskAndZipThem($finalFilePointer)
|
||||
{
|
||||
$worksheets = $this->getWorksheets();
|
||||
|
||||
$this->fileSystemHelper
|
||||
->createContentTypesFile($worksheets)
|
||||
->createWorkbookFile($worksheets)
|
||||
->createWorkbookRelsFile($worksheets)
|
||||
->createStylesFile($this->styleManager)
|
||||
->zipRootFolderAndCopyToStream($finalFilePointer)
|
||||
;
|
||||
}
|
||||
}
|
@ -1,376 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Writer\XLSX\Manager;
|
||||
|
||||
use OpenSpout\Common\Entity\Cell;
|
||||
use OpenSpout\Common\Entity\Row;
|
||||
use OpenSpout\Common\Entity\Style\Style;
|
||||
use OpenSpout\Common\Exception\InvalidArgumentException;
|
||||
use OpenSpout\Common\Exception\IOException;
|
||||
use OpenSpout\Common\Helper\Escaper\XLSX as XLSXEscaper;
|
||||
use OpenSpout\Common\Helper\StringHelper;
|
||||
use OpenSpout\Common\Manager\OptionsManagerInterface;
|
||||
use OpenSpout\Writer\Common\Entity\Options;
|
||||
use OpenSpout\Writer\Common\Entity\Worksheet;
|
||||
use OpenSpout\Writer\Common\Helper\CellHelper;
|
||||
use OpenSpout\Writer\Common\Manager\ManagesCellSize;
|
||||
use OpenSpout\Writer\Common\Manager\RegisteredStyle;
|
||||
use OpenSpout\Writer\Common\Manager\RowManager;
|
||||
use OpenSpout\Writer\Common\Manager\Style\StyleMerger;
|
||||
use OpenSpout\Writer\Common\Manager\WorksheetManagerInterface;
|
||||
use OpenSpout\Writer\XLSX\Helper\DateHelper;
|
||||
use OpenSpout\Writer\XLSX\Manager\Style\StyleManager;
|
||||
|
||||
/**
|
||||
* XLSX worksheet manager, providing the interfaces to work with XLSX worksheets.
|
||||
*/
|
||||
class WorksheetManager implements WorksheetManagerInterface
|
||||
{
|
||||
use ManagesCellSize;
|
||||
|
||||
/**
|
||||
* Maximum number of characters a cell can contain.
|
||||
*
|
||||
* @see https://support.office.com/en-us/article/Excel-specifications-and-limits-16c69c74-3d6a-4aaf-ba35-e6eb276e8eaa [Excel 2007]
|
||||
* @see https://support.office.com/en-us/article/Excel-specifications-and-limits-1672b34d-7043-467e-8e27-269d656771c3 [Excel 2010]
|
||||
* @see https://support.office.com/en-us/article/Excel-specifications-and-limits-ca36e2dc-1f09-4620-b726-67c00b05040f [Excel 2013/2016]
|
||||
*/
|
||||
public const MAX_CHARACTERS_PER_CELL = 32767;
|
||||
|
||||
public const SHEET_XML_FILE_HEADER = <<<'EOD'
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
|
||||
EOD;
|
||||
|
||||
/** @var bool Whether inline or shared strings should be used */
|
||||
protected $shouldUseInlineStrings;
|
||||
|
||||
/** @var OptionsManagerInterface */
|
||||
private $optionsManager;
|
||||
|
||||
/** @var RowManager Manages rows */
|
||||
private $rowManager;
|
||||
|
||||
/** @var StyleManager Manages styles */
|
||||
private $styleManager;
|
||||
|
||||
/** @var StyleMerger Helper to merge styles together */
|
||||
private $styleMerger;
|
||||
|
||||
/** @var SharedStringsManager Helper to write shared strings */
|
||||
private $sharedStringsManager;
|
||||
|
||||
/** @var XLSXEscaper Strings escaper */
|
||||
private $stringsEscaper;
|
||||
|
||||
/** @var StringHelper String helper */
|
||||
private $stringHelper;
|
||||
|
||||
/**
|
||||
* WorksheetManager constructor.
|
||||
*/
|
||||
public function __construct(
|
||||
OptionsManagerInterface $optionsManager,
|
||||
RowManager $rowManager,
|
||||
StyleManager $styleManager,
|
||||
StyleMerger $styleMerger,
|
||||
SharedStringsManager $sharedStringsManager,
|
||||
XLSXEscaper $stringsEscaper,
|
||||
StringHelper $stringHelper
|
||||
) {
|
||||
$this->optionsManager = $optionsManager;
|
||||
$this->shouldUseInlineStrings = $optionsManager->getOption(Options::SHOULD_USE_INLINE_STRINGS);
|
||||
$this->setDefaultColumnWidth($optionsManager->getOption(Options::DEFAULT_COLUMN_WIDTH));
|
||||
$this->setDefaultRowHeight($optionsManager->getOption(Options::DEFAULT_ROW_HEIGHT));
|
||||
$this->columnWidths = $optionsManager->getOption(Options::COLUMN_WIDTHS) ?? [];
|
||||
$this->rowManager = $rowManager;
|
||||
$this->styleManager = $styleManager;
|
||||
$this->styleMerger = $styleMerger;
|
||||
$this->sharedStringsManager = $sharedStringsManager;
|
||||
$this->stringsEscaper = $stringsEscaper;
|
||||
$this->stringHelper = $stringHelper;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SharedStringsManager
|
||||
*/
|
||||
public function getSharedStringsManager()
|
||||
{
|
||||
return $this->sharedStringsManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function startSheet(Worksheet $worksheet)
|
||||
{
|
||||
$sheetFilePointer = fopen($worksheet->getFilePath(), 'w');
|
||||
$this->throwIfSheetFilePointerIsNotAvailable($sheetFilePointer);
|
||||
|
||||
$worksheet->setFilePointer($sheetFilePointer);
|
||||
|
||||
fwrite($sheetFilePointer, self::SHEET_XML_FILE_HEADER);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addRow(Worksheet $worksheet, Row $row)
|
||||
{
|
||||
if (!$this->rowManager->isEmpty($row)) {
|
||||
$this->addNonEmptyRow($worksheet, $row);
|
||||
}
|
||||
|
||||
$worksheet->setLastWrittenRowIndex($worksheet->getLastWrittenRowIndex() + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct column width references xml to inject into worksheet xml file.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getXMLFragmentForColumnWidths()
|
||||
{
|
||||
if (empty($this->columnWidths)) {
|
||||
return '';
|
||||
}
|
||||
$xml = '<cols>';
|
||||
foreach ($this->columnWidths as $entry) {
|
||||
$xml .= '<col min="'.$entry[0].'" max="'.$entry[1].'" width="'.$entry[2].'" customWidth="true"/>';
|
||||
}
|
||||
$xml .= '</cols>';
|
||||
|
||||
return $xml;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs default row height and width xml to inject into worksheet xml file.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getXMLFragmentForDefaultCellSizing()
|
||||
{
|
||||
$rowHeightXml = empty($this->defaultRowHeight) ? '' : " defaultRowHeight=\"{$this->defaultRowHeight}\"";
|
||||
$colWidthXml = empty($this->defaultColumnWidth) ? '' : " defaultColWidth=\"{$this->defaultColumnWidth}\"";
|
||||
if (empty($colWidthXml) && empty($rowHeightXml)) {
|
||||
return '';
|
||||
}
|
||||
// Ensure that the required defaultRowHeight is set
|
||||
$rowHeightXml = empty($rowHeightXml) ? ' defaultRowHeight="0"' : $rowHeightXml;
|
||||
|
||||
return "<sheetFormatPr{$colWidthXml}{$rowHeightXml}/>";
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function close(Worksheet $worksheet)
|
||||
{
|
||||
$worksheetFilePointer = $worksheet->getFilePointer();
|
||||
|
||||
if (!\is_resource($worksheetFilePointer)) {
|
||||
return;
|
||||
}
|
||||
$this->ensureSheetDataStated($worksheet);
|
||||
fwrite($worksheetFilePointer, '</sheetData>');
|
||||
|
||||
// create nodes for merge cells
|
||||
if ($this->optionsManager->getOption(Options::MERGE_CELLS)) {
|
||||
$mergeCellString = '<mergeCells count="'.\count($this->optionsManager->getOption(Options::MERGE_CELLS)).'">';
|
||||
foreach ($this->optionsManager->getOption(Options::MERGE_CELLS) as $values) {
|
||||
$output = array_map(function ($value) {
|
||||
return CellHelper::getColumnLettersFromColumnIndex($value[0]).$value[1];
|
||||
}, $values);
|
||||
$mergeCellString .= '<mergeCell ref="'.implode(':', $output).'"/>';
|
||||
}
|
||||
$mergeCellString .= '</mergeCells>';
|
||||
fwrite($worksheet->getFilePointer(), $mergeCellString);
|
||||
}
|
||||
|
||||
fwrite($worksheetFilePointer, '</worksheet>');
|
||||
fclose($worksheetFilePointer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the sheet data header.
|
||||
*
|
||||
* @param Worksheet $worksheet The worksheet to add the row to
|
||||
*/
|
||||
private function ensureSheetDataStated(Worksheet $worksheet)
|
||||
{
|
||||
if (!$worksheet->getSheetDataStarted()) {
|
||||
$worksheetFilePointer = $worksheet->getFilePointer();
|
||||
$sheet = $worksheet->getExternalSheet();
|
||||
if ($sheet->hasSheetView()) {
|
||||
fwrite($worksheetFilePointer, '<sheetViews>'.$sheet->getSheetView()->getXml().'</sheetViews>');
|
||||
}
|
||||
fwrite($worksheetFilePointer, $this->getXMLFragmentForDefaultCellSizing());
|
||||
fwrite($worksheetFilePointer, $this->getXMLFragmentForColumnWidths());
|
||||
fwrite($worksheetFilePointer, '<sheetData>');
|
||||
$worksheet->setSheetDataStarted(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the sheet has been sucessfully created. Throws an exception if not.
|
||||
*
|
||||
* @param bool|resource $sheetFilePointer Pointer to the sheet data file or FALSE if unable to open the file
|
||||
*
|
||||
* @throws IOException If the sheet data file cannot be opened for writing
|
||||
*/
|
||||
private function throwIfSheetFilePointerIsNotAvailable($sheetFilePointer)
|
||||
{
|
||||
if (!$sheetFilePointer) {
|
||||
throw new IOException('Unable to open sheet for writing.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds non empty row to the worksheet.
|
||||
*
|
||||
* @param Worksheet $worksheet The worksheet to add the row to
|
||||
* @param Row $row The row to be written
|
||||
*
|
||||
* @throws InvalidArgumentException If a cell value's type is not supported
|
||||
* @throws IOException If the data cannot be written
|
||||
*/
|
||||
private function addNonEmptyRow(Worksheet $worksheet, Row $row)
|
||||
{
|
||||
$this->ensureSheetDataStated($worksheet);
|
||||
$sheetFilePointer = $worksheet->getFilePointer();
|
||||
$rowStyle = $row->getStyle();
|
||||
$rowIndexOneBased = $worksheet->getLastWrittenRowIndex() + 1;
|
||||
$numCells = $row->getNumCells();
|
||||
|
||||
$hasCustomHeight = $this->defaultRowHeight > 0 ? '1' : '0';
|
||||
$rowXML = "<row r=\"{$rowIndexOneBased}\" spans=\"1:{$numCells}\" customHeight=\"{$hasCustomHeight}\">";
|
||||
|
||||
foreach ($row->getCells() as $columnIndexZeroBased => $cell) {
|
||||
$registeredStyle = $this->applyStyleAndRegister($cell, $rowStyle);
|
||||
$cellStyle = $registeredStyle->getStyle();
|
||||
if ($registeredStyle->isMatchingRowStyle()) {
|
||||
$rowStyle = $cellStyle; // Replace actual rowStyle (possibly with null id) by registered style (with id)
|
||||
}
|
||||
$rowXML .= $this->getCellXML($rowIndexOneBased, $columnIndexZeroBased, $cell, $cellStyle->getId());
|
||||
}
|
||||
|
||||
$rowXML .= '</row>';
|
||||
|
||||
$wasWriteSuccessful = fwrite($sheetFilePointer, $rowXML);
|
||||
if (false === $wasWriteSuccessful) {
|
||||
throw new IOException("Unable to write data in {$worksheet->getFilePath()}");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies styles to the given style, merging the cell's style with its row's style.
|
||||
*
|
||||
* @throws InvalidArgumentException If the given value cannot be processed
|
||||
*/
|
||||
private function applyStyleAndRegister(Cell $cell, Style $rowStyle): RegisteredStyle
|
||||
{
|
||||
$isMatchingRowStyle = false;
|
||||
if ($cell->getStyle()->isEmpty()) {
|
||||
$cell->setStyle($rowStyle);
|
||||
|
||||
$possiblyUpdatedStyle = $this->styleManager->applyExtraStylesIfNeeded($cell);
|
||||
|
||||
if ($possiblyUpdatedStyle->isUpdated()) {
|
||||
$registeredStyle = $this->styleManager->registerStyle($possiblyUpdatedStyle->getStyle());
|
||||
} else {
|
||||
$registeredStyle = $this->styleManager->registerStyle($rowStyle);
|
||||
$isMatchingRowStyle = true;
|
||||
}
|
||||
} else {
|
||||
$mergedCellAndRowStyle = $this->styleMerger->merge($cell->getStyle(), $rowStyle);
|
||||
$cell->setStyle($mergedCellAndRowStyle);
|
||||
|
||||
$possiblyUpdatedStyle = $this->styleManager->applyExtraStylesIfNeeded($cell);
|
||||
|
||||
if ($possiblyUpdatedStyle->isUpdated()) {
|
||||
$newCellStyle = $possiblyUpdatedStyle->getStyle();
|
||||
} else {
|
||||
$newCellStyle = $mergedCellAndRowStyle;
|
||||
}
|
||||
|
||||
$registeredStyle = $this->styleManager->registerStyle($newCellStyle);
|
||||
}
|
||||
|
||||
return new RegisteredStyle($registeredStyle, $isMatchingRowStyle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and returns xml for a single cell.
|
||||
*
|
||||
* @param int $rowIndexOneBased
|
||||
* @param int $columnIndexZeroBased
|
||||
* @param int $styleId
|
||||
*
|
||||
* @throws InvalidArgumentException If the given value cannot be processed
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getCellXML($rowIndexOneBased, $columnIndexZeroBased, Cell $cell, $styleId)
|
||||
{
|
||||
$columnLetters = CellHelper::getColumnLettersFromColumnIndex($columnIndexZeroBased);
|
||||
$cellXML = '<c r="'.$columnLetters.$rowIndexOneBased.'"';
|
||||
$cellXML .= ' s="'.$styleId.'"';
|
||||
|
||||
if ($cell->isString()) {
|
||||
$cellXML .= $this->getCellXMLFragmentForNonEmptyString($cell->getValue());
|
||||
} elseif ($cell->isBoolean()) {
|
||||
$cellXML .= ' t="b"><v>'.(int) ($cell->getValue()).'</v></c>';
|
||||
} elseif ($cell->isNumeric()) {
|
||||
$cellXML .= '><v>'.$this->stringHelper->formatNumericValue($cell->getValue()).'</v></c>';
|
||||
} elseif ($cell->isFormula()) {
|
||||
$cellXML .= '><f>'.substr($cell->getValue(), 1).'</f></c>';
|
||||
} elseif ($cell->isDate()) {
|
||||
$value = $cell->getValue();
|
||||
if ($value instanceof \DateTimeInterface) {
|
||||
$cellXML .= '><v>'.(string) DateHelper::toExcel($value).'</v></c>';
|
||||
} else {
|
||||
throw new InvalidArgumentException('Trying to add a date value with an unsupported type: '.\gettype($value));
|
||||
}
|
||||
} elseif ($cell->isError() && \is_string($cell->getValueEvenIfError())) {
|
||||
// only writes the error value if it's a string
|
||||
$cellXML .= ' t="e"><v>'.$cell->getValueEvenIfError().'</v></c>';
|
||||
} elseif ($cell->isEmpty()) {
|
||||
if ($this->styleManager->shouldApplyStyleOnEmptyCell($styleId)) {
|
||||
$cellXML .= '/>';
|
||||
} else {
|
||||
// don't write empty cells that do no need styling
|
||||
// NOTE: not appending to $cellXML is the right behavior!!
|
||||
$cellXML = '';
|
||||
}
|
||||
} else {
|
||||
throw new InvalidArgumentException('Trying to add a value with an unsupported type: '.\gettype($cell->getValue()));
|
||||
}
|
||||
|
||||
return $cellXML;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the XML fragment for a cell containing a non empty string.
|
||||
*
|
||||
* @param string $cellValue The cell value
|
||||
*
|
||||
* @throws InvalidArgumentException If the string exceeds the maximum number of characters allowed per cell
|
||||
*
|
||||
* @return string The XML fragment representing the cell
|
||||
*/
|
||||
private function getCellXMLFragmentForNonEmptyString($cellValue)
|
||||
{
|
||||
if ($this->stringHelper->getStringLength($cellValue) > self::MAX_CHARACTERS_PER_CELL) {
|
||||
throw new InvalidArgumentException('Trying to add a value that exceeds the maximum number of characters allowed in a cell (32,767)');
|
||||
}
|
||||
|
||||
if ($this->shouldUseInlineStrings) {
|
||||
$cellXMLFragment = ' t="inlineStr"><is><t>'.$this->stringsEscaper->escape($cellValue).'</t></is></c>';
|
||||
} else {
|
||||
$sharedStringId = $this->sharedStringsManager->writeString($cellValue);
|
||||
$cellXMLFragment = ' t="s"><v>'.$sharedStringId.'</v></c>';
|
||||
}
|
||||
|
||||
return $cellXMLFragment;
|
||||
}
|
||||
}
|
@ -1,72 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OpenSpout\Writer\XLSX;
|
||||
|
||||
use OpenSpout\Writer\Common\Entity\Options;
|
||||
use OpenSpout\Writer\WriterMultiSheetsAbstract;
|
||||
|
||||
/**
|
||||
* This class provides base support to write data to XLSX files.
|
||||
*/
|
||||
class Writer extends WriterMultiSheetsAbstract
|
||||
{
|
||||
/** @var string Content-Type value for the header */
|
||||
protected static $headerContentType = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
|
||||
|
||||
/**
|
||||
* Sets a custom temporary folder for creating intermediate files/folders.
|
||||
* This must be set before opening the writer.
|
||||
*
|
||||
* @param string $tempFolder Temporary folder where the files to create the XLSX will be stored
|
||||
*
|
||||
* @throws \OpenSpout\Writer\Exception\WriterAlreadyOpenedException If the writer was already opened
|
||||
*
|
||||
* @return Writer
|
||||
*/
|
||||
public function setTempFolder($tempFolder)
|
||||
{
|
||||
$this->throwIfWriterAlreadyOpened('Writer must be configured before opening it.');
|
||||
|
||||
$this->optionsManager->setOption(Options::TEMP_FOLDER, $tempFolder);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use inline string to be more memory efficient. If set to false, it will use shared strings.
|
||||
* This must be set before opening the writer.
|
||||
*
|
||||
* @param bool $shouldUseInlineStrings Whether inline or shared strings should be used
|
||||
*
|
||||
* @throws \OpenSpout\Writer\Exception\WriterAlreadyOpenedException If the writer was already opened
|
||||
*
|
||||
* @return Writer
|
||||
*/
|
||||
public function setShouldUseInlineStrings($shouldUseInlineStrings)
|
||||
{
|
||||
$this->throwIfWriterAlreadyOpened('Writer must be configured before opening it.');
|
||||
|
||||
$this->optionsManager->setOption(Options::SHOULD_USE_INLINE_STRINGS, $shouldUseInlineStrings);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge cells.
|
||||
* Row coordinates are indexed from 1, columns from 0 (A = 0),
|
||||
* so a merge B2:G2 looks like $writer->mergeCells([1,2], [6, 2]);.
|
||||
*
|
||||
* You may use CellHelper::getColumnLettersFromColumnIndex() to convert from "B2" to "[1,2]"
|
||||
*
|
||||
* @param int[] $range1 - top left cell's coordinate [column, row]
|
||||
* @param int[] $range2 - bottom right cell's coordinate [column, row]
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function mergeCells(array $range1, array $range2)
|
||||
{
|
||||
$this->optionsManager->addOption(Options::MERGE_CELLS, [$range1, $range2]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
38
upstream-4.x/README.md
Normal file
38
upstream-4.x/README.md
Normal file
@ -0,0 +1,38 @@
|
||||
# OpenSpout
|
||||
|
||||
[](https://packagist.org/packages/openspout/openspout)
|
||||
[](https://packagist.org/packages/openspout/openspout)
|
||||
[](https://github.com/openspout/openspout/actions/workflows/ci.yml)
|
||||
[](https://dashboard.stryker-mutator.io/reports/github.com/openspout/openspout/4.x)
|
||||
|
||||
OpenSpout is a community driven fork of `box/spout`, a PHP library to read and write spreadsheet files
|
||||
(CSV, XLSX and ODS), in a fast and scalable way. Unlike other file readers or writers, it is capable of processing
|
||||
very large files, while keeping the memory usage really low (less than 3MB).
|
||||
|
||||
## Documentation
|
||||
|
||||
Documentation can be found at [`docs/`](docs).
|
||||
|
||||
## Upgrade from `box/spout:v3` to `openspout/openspout:v3`
|
||||
|
||||
1. Replace `box/spout` with `openspout/openspout` in your `composer.json`
|
||||
2. Replace `Box\Spout` with `OpenSpout` in your code
|
||||
|
||||
## Upgrade guide
|
||||
|
||||
Version 4 introduced new functionality but also some breaking changes. If you want to upgrade your OpenSpout codebase
|
||||
please consult the [Upgrade guide](UPGRADE.md).
|
||||
|
||||
## Copyright and License
|
||||
|
||||
This is a fork of Box's Spout library: https://github.com/box/spout
|
||||
|
||||
Code until and directly descending from commit [`cc42c1d`](https://github.com/openspout/openspout/commit/cc42c1d29fc5d29f07caeace99bd29dbb6d7c2f8)
|
||||
is copyright of _Box, Inc._ and licensed under the Apache License, Version 2.0:
|
||||
|
||||
https://github.com/openspout/openspout/blob/cc42c1d29fc5d29f07caeace99bd29dbb6d7c2f8/LICENSE
|
||||
|
||||
Code created, edited and released after the commit mentioned above
|
||||
is copyright of _openspout_ Github organization and licensed under MIT License.
|
||||
|
||||
https://github.com/openspout/openspout/blob/main/LICENSE
|
@ -1,33 +1,103 @@
|
||||
Upgrading from 2.x to 3.0
|
||||
=========================
|
||||
# Upgrade guide
|
||||
|
||||
Spout 3.0 introduced several backwards-incompatible changes. The upgrade from Spout 2.x to 3.0 must therefore be done with caution.
|
||||
## Upgrading from 3.x to 4.0
|
||||
|
||||
Beginning with v4, only actively supported [PHP version](https://www.php.net/supported-versions.php) will be supported.
|
||||
Removing support for EOLed PHP versions as well adding support for new PHP versions will be included in MINOR releases.
|
||||
|
||||
### Most notable changes
|
||||
|
||||
1. OpenSpout is now fully typed
|
||||
2. Classes and interfaces not consumed by the user are now marked as `@internal`
|
||||
3. Classes used by the user are all `final`
|
||||
|
||||
### Reader & Writer objects
|
||||
|
||||
Both readers and writers have to be naturally instantiated with `new` keyword, passing the eventual needed `Options`
|
||||
class as the first argument:
|
||||
|
||||
```php
|
||||
use OpenSpout\Reader\CSV\Reader;
|
||||
use OpenSpout\Reader\CSV\Options;
|
||||
|
||||
$options = new Options();
|
||||
$options->FIELD_DELIMITER = '|';
|
||||
$options->FIELD_ENCLOSURE = '@';
|
||||
$reader = new Reader($options);
|
||||
```
|
||||
|
||||
### Cell types on writes
|
||||
|
||||
Cell types are now handled with separate classes:
|
||||
|
||||
```php
|
||||
use OpenSpout\Common\Entity\Cell;
|
||||
use OpenSpout\Common\Entity\Row;
|
||||
|
||||
$row = new Row([
|
||||
new Cell\BooleanCell(true),
|
||||
new Cell\DateIntervalCell(new DateInterval('P1D')),
|
||||
new Cell\DateTimeCell(new DateTimeImmutable('now')),
|
||||
new Cell\EmptyCell(null),
|
||||
new Cell\FormulaCell('=SUM(A1:A2)'),
|
||||
new Cell\NumericCell(3),
|
||||
new Cell\StringCell('foo'),
|
||||
]);
|
||||
```
|
||||
|
||||
Auto-typing is still available though:
|
||||
|
||||
```php
|
||||
use OpenSpout\Common\Entity\Cell;
|
||||
use OpenSpout\Common\Entity\Row;
|
||||
|
||||
$cell = Cell::fromValue(true); // Instance of Cell\BooleanCell
|
||||
|
||||
$row = Row::fromValues([
|
||||
true,
|
||||
new DateInterval('P1D'),
|
||||
new DateTimeImmutable('now'),
|
||||
null,
|
||||
'=SUM(A1:A2)',
|
||||
3,
|
||||
'foo',
|
||||
]);
|
||||
```
|
||||
|
||||
## Upgrading from 2.x to 3.0
|
||||
|
||||
OpenSpout 3.0 introduced several backwards-incompatible changes. The upgrade from OpenSpout 2.x to 3.0 must therefore
|
||||
be done with caution.
|
||||
This guide is meant to ease this process.
|
||||
|
||||
Most notable changes
|
||||
--------------------
|
||||
### Most notable changes
|
||||
|
||||
In 2.x, styles were applied per row; it was therefore impossible to apply different styles to cells in the same row.
|
||||
With the 3.0 version, this is now possible: each cell can have its own style.
|
||||
|
||||
Spout 3.0 tries to enforce better typing. For instance, instead of using/returning generic arrays, Spout now makes use of specific `Row` and `Cell` objects that can encapsulate more data such as type, style, value.
|
||||
OpenSpout 3.0 tries to enforce better typing. For instance, instead of using/returning generic arrays, OpenSpout now
|
||||
makes use of specific `Row` and `Cell` objects that can encapsulate more data such as type, style, value.
|
||||
|
||||
Finally, **_Spout 3.2 only supports PHP 7.2 and above_**, as other PHP versions are no longer supported by the community.
|
||||
Finally, **_OpenSpout 3.2 only supports PHP 7.2 and above_**, as other PHP versions are no longer supported by the
|
||||
community.
|
||||
|
||||
### Reader changes
|
||||
|
||||
Reader changes
|
||||
--------------
|
||||
Creating a reader should now be done through the Reader `ReaderEntityFactory`, instead of using the `ReaderFactory`.
|
||||
Also, the `ReaderFactory::create($type)` method was removed and replaced by methods for each reader:
|
||||
|
||||
```php
|
||||
use OpenSpout\Reader\Common\Creator\ReaderEntityFactory; // namespace is no longer "OpenSpout\Reader"
|
||||
...
|
||||
|
||||
$reader = ReaderEntityFactory::createXLSXReader(); // replaces ReaderFactory::create(Type::XLSX)
|
||||
$reader = ReaderEntityFactory::createCSVReader(); // replaces ReaderFactory::create(Type::CSV)
|
||||
$reader = ReaderEntityFactory::createODSReader(); // replaces ReaderFactory::create(Type::ODS)
|
||||
```
|
||||
|
||||
When iterating over the spreadsheet rows, Spout now returns `Row` objects, instead of an array containing row values. Accessing the row values should now be done this way:
|
||||
When iterating over the spreadsheet rows, OpenSpout now returns `Row` objects, instead of an array containing row
|
||||
values. Accessing the row values should now be done this way:
|
||||
|
||||
```php
|
||||
...
|
||||
foreach ($reader->getSheetIterator() as $sheet) {
|
||||
foreach ($sheet->getRowIterator() as $row) { // $row is a "Row" object, not an array
|
||||
$rowAsArray = $row->toArray(); // this is the 2.x equivalent
|
||||
@ -38,20 +108,22 @@ foreach ($reader->getSheetIterator() as $sheet) {
|
||||
}
|
||||
```
|
||||
|
||||
Writer changes
|
||||
--------------
|
||||
Writer creation follows the same change as the reader. It should now be done through the Writer `WriterEntityFactory`, instead of using the `WriterFactory`.
|
||||
### Writer changes
|
||||
|
||||
Writer creation follows the same change as the reader. It should now be done through the Writer `WriterEntityFactory`,
|
||||
instead of using the `WriterFactory`.
|
||||
Also, the `WriterFactory::create($type)` method was removed and replaced by methods for each writer:
|
||||
|
||||
```php
|
||||
use OpenSpout\Writer\Common\Creator\WriterEntityFactory; // namespace is no longer "OpenSpout\Writer"
|
||||
...
|
||||
|
||||
$writer = WriterEntityFactory::createXLSXWriter(); // replaces WriterFactory::create(Type::XLSX)
|
||||
$writer = WriterEntityFactory::createCSVWriter(); // replaces WriterFactory::create(Type::CSV)
|
||||
$writer = WriterEntityFactory::createODSWriter(); // replaces WriterFactory::create(Type::ODS)
|
||||
```
|
||||
|
||||
Adding rows is also done differently: instead of passing an array, the writer now takes in a `Row` object (or an array of `Row`). Creating such objects can easily be done this way:
|
||||
Adding rows is also done differently: instead of passing an array, the writer now takes in a `Row` object (or an
|
||||
array of `Row`). Creating such objects can easily be done this way:
|
||||
```php
|
||||
// Adding a row from an array of values (2.x equivalent)
|
||||
$cellValues = ['foo', 12345];
|
||||
@ -65,8 +137,8 @@ $row2 = WriterEntityFactory::createRow([$cell1, $cell2]);
|
||||
$writer->addRows([$row1, $row2]);
|
||||
```
|
||||
|
||||
Namespace changes for styles
|
||||
-----------------
|
||||
### Namespace changes for styles
|
||||
|
||||
The namespaces for styles have changed. Styles are still created by using a `builder` class.
|
||||
|
||||
For the builder, please update your import statements to use the following namespaces:
|
||||
@ -83,7 +155,8 @@ If your are using these classes directly via an import statement in your code, p
|
||||
OpenSpout\Common\Entity\Style\Color
|
||||
OpenSpout\Common\Entity\Style\Style
|
||||
|
||||
Handling of empty rows
|
||||
----------------------
|
||||
### Handling of empty rows
|
||||
|
||||
In 2.x, empty rows were not added to the spreadsheet.
|
||||
In 3.0, `addRow` now always writes a row to the spreadsheet: when the row does not contain any cells, an empty row is created in the sheet.
|
||||
In 3.0, `addRow` now always writes a row to the spreadsheet: when the row does not contain any cells, an empty row
|
||||
is created in the sheet.
|
@ -28,8 +28,9 @@
|
||||
],
|
||||
"homepage": "https://github.com/openspout/openspout",
|
||||
"require": {
|
||||
"php": "~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0",
|
||||
"php": "~8.2.0 || ~8.3.0 || ~8.4.0",
|
||||
"ext-dom": "*",
|
||||
"ext-fileinfo": "*",
|
||||
"ext-filter": "*",
|
||||
"ext-libxml": "*",
|
||||
"ext-xmlreader": "*",
|
||||
@ -37,14 +38,17 @@
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-zlib": "*",
|
||||
"friendsofphp/php-cs-fixer": "^3.4",
|
||||
"phpstan/phpstan": "^1.4",
|
||||
"phpstan/phpstan-phpunit": "^1.0",
|
||||
"phpunit/phpunit": "^9.5"
|
||||
"friendsofphp/php-cs-fixer": "^3.65.0",
|
||||
"infection/infection": "^0.29.8",
|
||||
"phpbench/phpbench": "^1.3.1",
|
||||
"phpstan/phpstan": "^2.0.2",
|
||||
"phpstan/phpstan-phpunit": "^2.0.1",
|
||||
"phpstan/phpstan-strict-rules": "^2",
|
||||
"phpunit/phpunit": "^11.4.3"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-iconv": "To handle non UTF-8 CSV files (if \"php-intl\" is not already installed or is too limited)",
|
||||
"ext-intl": "To handle non UTF-8 CSV files (if \"iconv\" is not already installed)"
|
||||
"ext-iconv": "To handle non UTF-8 CSV files (if \"php-mbstring\" is not already installed or is too limited)",
|
||||
"ext-mbstring": "To handle non UTF-8 CSV files (if \"iconv\" is not already installed)"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
@ -52,13 +56,16 @@
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"OpenSpout\\Benchmarks\\": "benchmarks/"
|
||||
},
|
||||
"classmap": [
|
||||
"tests/"
|
||||
]
|
||||
},
|
||||
"config": {
|
||||
"platform": {
|
||||
"php": "7.3"
|
||||
"allow-plugins": {
|
||||
"infection/extension-installer": true
|
||||
}
|
||||
},
|
||||
"extra": {
|
6
upstream-4.x/renovate.json
Normal file
6
upstream-4.x/renovate.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"local>Slamdunk/.github:renovate-config"
|
||||
]
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user