diff --git a/dataformat/csv/classes/writer.php b/dataformat/csv/classes/writer.php
index 36b9eac858a54..79562169620bf 100644
--- a/dataformat/csv/classes/writer.php
+++ b/dataformat/csv/classes/writer.php
@@ -42,7 +42,7 @@ class writer extends \core\dataformat\spout_base {
protected $extension = ".csv";
/** @var $spouttype */
- protected $spouttype = \Box\Spout\Common\Type::CSV;
+ protected $spouttype = \OpenSpout\Common\Type::CSV;
}
diff --git a/dataformat/excel/classes/writer.php b/dataformat/excel/classes/writer.php
index 269e9b87a3427..5e73a4e94cedc 100644
--- a/dataformat/excel/classes/writer.php
+++ b/dataformat/excel/classes/writer.php
@@ -42,7 +42,7 @@ class writer extends \core\dataformat\spout_base {
protected $extension = ".xlsx";
/** @var $spouttype */
- protected $spouttype = \Box\Spout\Common\Type::XLSX;
+ protected $spouttype = \OpenSpout\Common\Type::XLSX;
/**
* Set the title of the worksheet inside a spreadsheet
diff --git a/dataformat/excel/tests/writer_test.php b/dataformat/excel/tests/writer_test.php
index ec9fce708ba9c..1a8e139442d17 100644
--- a/dataformat/excel/tests/writer_test.php
+++ b/dataformat/excel/tests/writer_test.php
@@ -57,15 +57,15 @@ private function get_excel(string $content) {
$file = tempnam(sys_get_temp_dir(), 'excel_');
$handle = fopen($file, "w");
fwrite($handle, $content);
- /** @var \Box\Spout\Reader\XLSX\Reader $reader */
- $reader = \Box\Spout\Reader\Common\Creator\ReaderFactory::createFromType(\Box\Spout\Common\Type::XLSX);
+ /** @var \OpenSpout\Reader\XLSX\Reader $reader */
+ $reader = \OpenSpout\Reader\Common\Creator\ReaderFactory::createFromType(\OpenSpout\Common\Type::XLSX);
$reader->open($file);
- /** @var \Box\Spout\Reader\XLSX\Sheet[] $sheets */
+ /** @var \OpenSpout\Reader\XLSX\Sheet[] $sheets */
$sheets = $reader->getSheetIterator();
$rowscellsvalues = [];
foreach ($sheets as $sheet) {
- /** @var \Box\Spout\Common\Entity\Row[] $rows */
+ /** @var \OpenSpout\Common\Entity\Row[] $rows */
$rows = $sheet->getRowIterator();
foreach ($rows as $row) {
$thisvalues = [];
diff --git a/dataformat/ods/classes/writer.php b/dataformat/ods/classes/writer.php
index 9418abe652e0d..7c94d39a79abf 100644
--- a/dataformat/ods/classes/writer.php
+++ b/dataformat/ods/classes/writer.php
@@ -42,7 +42,7 @@ class writer extends \core\dataformat\spout_base {
protected $extension = ".ods";
/** @var $spouttype */
- protected $spouttype = \Box\Spout\Common\Type::ODS;
+ protected $spouttype = \OpenSpout\Common\Type::ODS;
/**
* Set the title of the worksheet inside a spreadsheet
diff --git a/dataformat/ods/tests/writer_test.php b/dataformat/ods/tests/writer_test.php
index fe71ab0f50b94..c905f1519a519 100644
--- a/dataformat/ods/tests/writer_test.php
+++ b/dataformat/ods/tests/writer_test.php
@@ -51,20 +51,20 @@ public function test_write_data(): void {
* Get ods rows from binary content
* @param string $content
* @return array
- * @throws \Box\Spout\Common\Exception\IOException
- * @throws \Box\Spout\Reader\Exception\ReaderNotOpenedException
+ * @throws \OpenSpout\Common\Exception\IOException
+ * @throws \OpenSpout\Reader\Exception\ReaderNotOpenedException
*/
private function get_ods_rows_content($content) {
- $reader = \Box\Spout\Reader\Common\Creator\ReaderFactory::createFromType(\Box\Spout\Common\Type::ODS);
+ $reader = \OpenSpout\Reader\Common\Creator\ReaderFactory::createFromType(\OpenSpout\Common\Type::ODS);
$file = tempnam(sys_get_temp_dir(), 'ods_');
$handle = fopen($file, "w");
fwrite($handle, $content);
$reader->open($file);
- /** @var \Box\Spout\Reader\ODS\Sheet[] $sheets */
+ /** @var \OpenSpout\Reader\ODS\Sheet[] $sheets */
$sheets = $reader->getSheetIterator();
$rowscellsvalues = [];
foreach ($sheets as $sheet) {
- /** @var \Box\Spout\Common\Entity\Row[] $rows */
+ /** @var \OpenSpout\Common\Entity\Row[] $rows */
$rows = $sheet->getRowIterator();
foreach ($rows as $row) {
$thisvalues = [];
diff --git a/lib/classes/component.php b/lib/classes/component.php
index 7e34fbd038637..9b25c6c2bbe12 100644
--- a/lib/classes/component.php
+++ b/lib/classes/component.php
@@ -97,7 +97,7 @@ class core_component {
'Sabberworm\\CSS' => 'lib/php-css-parser',
'MoodleHQ\\RTLCSS' => 'lib/rtlcss',
'ScssPhp\\ScssPhp' => 'lib/scssphp',
- 'Box\\Spout' => 'lib/spout/src/Spout',
+ 'OpenSpout' => 'lib/openspout/src',
'MatthiasMullie\\Minify' => 'lib/minify/matthiasmullie-minify/src/',
'MatthiasMullie\\PathConverter' => 'lib/minify/matthiasmullie-pathconverter/src/',
'IMSGlobal\LTI' => 'lib/ltiprovider/src',
diff --git a/lib/classes/dataformat/spout_base.php b/lib/classes/dataformat/spout_base.php
index 4c582707ec301..3f1ba427ef000 100644
--- a/lib/classes/dataformat/spout_base.php
+++ b/lib/classes/dataformat/spout_base.php
@@ -51,7 +51,7 @@ abstract class spout_base extends \core\dataformat\base {
* Output file headers to initialise the download of the file.
*/
public function send_http_headers() {
- $this->writer = \Box\Spout\Writer\Common\Creator\WriterEntityFactory::createWriter($this->spouttype);
+ $this->writer = \OpenSpout\Writer\Common\Creator\WriterEntityFactory::createWriter($this->spouttype);
if (method_exists($this->writer, 'setTempFolder')) {
$this->writer->setTempFolder(make_request_directory());
}
@@ -70,7 +70,7 @@ public function send_http_headers() {
* Set the dataformat to be output to current file
*/
public function start_output_to_file(): void {
- $this->writer = \Box\Spout\Writer\Common\Creator\WriterEntityFactory::createWriter($this->spouttype);
+ $this->writer = \OpenSpout\Writer\Common\Creator\WriterEntityFactory::createWriter($this->spouttype);
if (method_exists($this->writer, 'setTempFolder')) {
$this->writer->setTempFolder(make_request_directory());
}
@@ -100,7 +100,7 @@ public function set_sheettitle($title) {
* @param array $columns
*/
public function start_sheet($columns) {
- if ($this->sheettitle && $this->writer instanceof \Box\Spout\Writer\WriterMultiSheetsAbstract) {
+ if ($this->sheettitle && $this->writer instanceof \OpenSpout\Writer\WriterMultiSheetsAbstract) {
if ($this->renamecurrentsheet) {
$sheet = $this->writer->getCurrentSheet();
$this->renamecurrentsheet = false;
@@ -109,7 +109,7 @@ public function start_sheet($columns) {
}
$sheet->setName($this->sheettitle);
}
- $row = \Box\Spout\Writer\Common\Creator\WriterEntityFactory::createRowFromArray((array)$columns);
+ $row = \OpenSpout\Writer\Common\Creator\WriterEntityFactory::createRowFromArray((array)$columns);
$this->writer->addRow($row);
}
@@ -120,7 +120,7 @@ public function start_sheet($columns) {
* @param int $rownum
*/
public function write_record($record, $rownum) {
- $row = \Box\Spout\Writer\Common\Creator\WriterEntityFactory::createRowFromArray($this->format_record($record));
+ $row = \OpenSpout\Writer\Common\Creator\WriterEntityFactory::createRowFromArray($this->format_record($record));
$this->writer->addRow($row);
}
diff --git a/lib/db/renamedclasses.php b/lib/db/renamedclasses.php
index 6547bfd22c582..01b3ada5bff04 100644
--- a/lib/db/renamedclasses.php
+++ b/lib/db/renamedclasses.php
@@ -90,4 +90,6 @@
'core_cohort\\local\\entities\\cohort' => 'core_cohort\\reportbuilder\\local\\entities\\cohort',
'core_cohort\\local\\entities\\cohort_member' => 'core_cohort\\reportbuilder\\local\\entities\\cohort_member',
'core_block\\local\\views\\secondary' => 'core_block\\navigation\\views\\secondary',
+ // Since Moodle 4.2.
+ 'Box\\Spout' => 'OpenSpout',
];
diff --git a/lib/openspout/LICENSE b/lib/openspout/LICENSE
new file mode 100644
index 0000000000000..38ce746d64716
--- /dev/null
+++ b/lib/openspout/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2022 openspout
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/lib/openspout/README.md b/lib/openspout/README.md
new file mode 100644
index 0000000000000..0ceb637f69e2b
--- /dev/null
+++ b/lib/openspout/README.md
@@ -0,0 +1,54 @@
+# OpenSpout
+
+[![Latest Stable Version](https://poser.pugx.org/openspout/openspout/v/stable)](https://packagist.org/packages/openspout/openspout)
+[![Build Status](https://github.com/openspout/openspout/actions/workflows/ci.yml/badge.svg)](https://github.com/openspout/openspout/actions/workflows/ci.yml)
+[![Code Coverage](https://codecov.io/gh/openspout/openspout/coverage.svg?branch=main)](https://codecov.io/gh/openspout/openspout?branch=main)
+[![Total Downloads](https://poser.pugx.org/openspout/openspout/downloads)](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: [![Build Status](https://travis-ci.org/box/spout.svg?branch=perf-tests)](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
diff --git a/lib/openspout/readme_moodle.txt b/lib/openspout/readme_moodle.txt
new file mode 100644
index 0000000000000..eade7a0640675
--- /dev/null
+++ b/lib/openspout/readme_moodle.txt
@@ -0,0 +1,10 @@
+OpenSpout
+---------
+
+Downloaded from: https://github.com/openspout/openspout
+
+Import procedure:
+
+* Copy all the files from the folder 'src' directory.
+* Copy the LICENSE & README.md files from the project root.
+* Update lib/thirdpartylibs.xml with the latest version.
diff --git a/lib/spout/src/Spout/Autoloader/Psr4Autoloader.php b/lib/openspout/src/Autoloader/Psr4Autoloader.php
similarity index 63%
rename from lib/spout/src/Spout/Autoloader/Psr4Autoloader.php
rename to lib/openspout/src/Autoloader/Psr4Autoloader.php
index 37b6d4ac1092f..59eac31569649 100644
--- a/lib/spout/src/Spout/Autoloader/Psr4Autoloader.php
+++ b/lib/openspout/src/Autoloader/Psr4Autoloader.php
@@ -1,9 +1,8 @@
prefixes[$prefix]) === false) {
+ 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);
+ array_unshift($this->prefixes[$prefix], $baseDir);
} else {
- \array_push($this->prefixes[$prefix], $baseDir);
+ $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.
+ * @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)
{
@@ -72,22 +69,22 @@ public function loadClass($class)
// work backwards through the namespace names of the fully-qualified
// class name to find a mapped file name
- while (($pos = \strrpos($prefix, '\\')) !== false) {
+ while (($pos = strrpos($prefix, '\\')) !== false) {
// retain the trailing namespace separator in the prefix
- $prefix = \substr($class, 0, $pos + 1);
+ $prefix = substr($class, 0, $pos + 1);
// the rest is the relative class name
- $relativeClass = \substr($class, $pos + 1);
+ $relativeClass = substr($class, $pos + 1);
// try to load a mapped file for the prefix and relative class
$mappedFile = $this->loadMappedFile($prefix, $relativeClass);
- if ($mappedFile !== false) {
+ if (false !== $mappedFile) {
return $mappedFile;
}
// remove the trailing namespace separator for the next iteration
// of strrpos()
- $prefix = \rtrim($prefix, '\\');
+ $prefix = rtrim($prefix, '\\');
}
// never found a mapped file
@@ -97,15 +94,16 @@ public function loadClass($class)
/**
* 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.
+ * @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 (isset($this->prefixes[$prefix]) === false) {
+ if (false === isset($this->prefixes[$prefix])) {
return false;
}
@@ -115,8 +113,8 @@ protected function loadMappedFile($prefix, $relativeClass)
// replace namespace separators with directory separators
// in the relative class name, append with .php
$file = $baseDir
- . \str_replace('\\', '/', $relativeClass)
- . '.php';
+ .str_replace('\\', '/', $relativeClass)
+ .'.php';
// if the mapped file exists, require it
if ($this->requireFile($file)) {
@@ -132,12 +130,13 @@ protected function loadMappedFile($prefix, $relativeClass)
/**
* 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.
+ * @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)) {
+ if (file_exists($file)) {
require $file;
return true;
diff --git a/lib/openspout/src/Autoloader/autoload.php b/lib/openspout/src/Autoloader/autoload.php
new file mode 100644
index 0000000000000..a6768d4e84909
--- /dev/null
+++ b/lib/openspout/src/Autoloader/autoload.php
@@ -0,0 +1,15 @@
+register();
+$loader->addNamespace('OpenSpout', $srcBaseDirectory);
diff --git a/lib/spout/src/Spout/Common/Creator/HelperFactory.php b/lib/openspout/src/Common/Creator/HelperFactory.php
similarity index 70%
rename from lib/spout/src/Spout/Common/Creator/HelperFactory.php
rename to lib/openspout/src/Common/Creator/HelperFactory.php
index 2277f2c86188b..55f1f57bf7e4b 100644
--- a/lib/spout/src/Spout/Common/Creator/HelperFactory.php
+++ b/lib/openspout/src/Common/Creator/HelperFactory.php
@@ -1,15 +1,14 @@
getValue();
+ }
+
+ /**
+ * @param null|mixed $value
*/
public function setValue($value)
{
@@ -84,7 +91,7 @@ public function setValue($value)
}
/**
- * @return mixed|null
+ * @return null|mixed
*/
public function getValue()
{
@@ -100,7 +107,7 @@ public function getValueEvenIfError()
}
/**
- * @param Style|null $style
+ * @param null|Style $style
*/
public function setStyle($style)
{
@@ -116,7 +123,7 @@ public function getStyle()
}
/**
- * @return int|null
+ * @return null|int
*/
public function getType()
{
@@ -131,39 +138,12 @@ public function setType($type)
$this->type = $type;
}
- /**
- * Get the current value type
- *
- * @param mixed|null $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::isNonEmptyString($value)) {
- return self::TYPE_STRING;
- }
-
- return self::TYPE_ERROR;
- }
-
/**
* @return bool
*/
public function isBoolean()
{
- return $this->type === self::TYPE_BOOLEAN;
+ return self::TYPE_BOOLEAN === $this->type;
}
/**
@@ -171,7 +151,7 @@ public function isBoolean()
*/
public function isEmpty()
{
- return $this->type === self::TYPE_EMPTY;
+ return self::TYPE_EMPTY === $this->type;
}
/**
@@ -179,7 +159,7 @@ public function isEmpty()
*/
public function isNumeric()
{
- return $this->type === self::TYPE_NUMERIC;
+ return self::TYPE_NUMERIC === $this->type;
}
/**
@@ -187,7 +167,7 @@ public function isNumeric()
*/
public function isString()
{
- return $this->type === self::TYPE_STRING;
+ return self::TYPE_STRING === $this->type;
}
/**
@@ -195,7 +175,15 @@ public function isString()
*/
public function isDate()
{
- return $this->type === self::TYPE_DATE;
+ return self::TYPE_DATE === $this->type;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isFormula()
+ {
+ return self::TYPE_FORMULA === $this->type;
}
/**
@@ -203,14 +191,37 @@ public function isDate()
*/
public function isError()
{
- return $this->type === self::TYPE_ERROR;
+ return self::TYPE_ERROR === $this->type;
}
/**
- * @return string
+ * Get the current value type.
+ *
+ * @param null|mixed $value
+ *
+ * @return int
*/
- public function __toString()
+ protected function detectType($value)
{
- return (string) $this->getValue();
+ 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;
}
}
diff --git a/lib/spout/src/Spout/Common/Entity/Row.php b/lib/openspout/src/Common/Entity/Row.php
similarity index 67%
rename from lib/spout/src/Spout/Common/Entity/Row.php
rename to lib/openspout/src/Common/Entity/Row.php
index 0cc49c7309b5d..db6481fc4df10 100644
--- a/lib/spout/src/Spout/Common/Entity/Row.php
+++ b/lib/openspout/src/Common/Entity/Row.php
@@ -1,33 +1,44 @@
setCells($cells)
- ->setStyle($style);
+ ->setStyle($style)
+ ;
}
/**
@@ -40,6 +51,7 @@ public function getCells()
/**
* @param Cell[] $cells
+ *
* @return Row
*/
public function setCells(array $cells)
@@ -53,8 +65,8 @@ public function setCells(array $cells)
}
/**
- * @param Cell $cell
* @param int $cellIndex
+ *
* @return Row
*/
public function setCellAtIndex(Cell $cell, $cellIndex)
@@ -66,7 +78,8 @@ public function setCellAtIndex(Cell $cell, $cellIndex)
/**
* @param int $cellIndex
- * @return Cell|null
+ *
+ * @return null|Cell
*/
public function getCellAtIndex($cellIndex)
{
@@ -74,7 +87,6 @@ public function getCellAtIndex($cellIndex)
}
/**
- * @param Cell $cell
* @return Row
*/
public function addCell(Cell $cell)
@@ -95,7 +107,7 @@ public function getNumCells()
return 0;
}
- return \max(\array_keys($this->cells)) + 1;
+ return max(array_keys($this->cells)) + 1;
}
/**
@@ -107,7 +119,8 @@ public function getStyle()
}
/**
- * @param Style|null $style
+ * @param null|Style $style
+ *
* @return Row
*/
public function setStyle($style)
@@ -122,8 +135,32 @@ public function setStyle($style)
*/
public function toArray()
{
- return \array_map(function (Cell $cell) {
+ 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;
+ }
}
diff --git a/lib/spout/src/Spout/Common/Entity/Style/Border.php b/lib/openspout/src/Common/Entity/Style/Border.php
similarity index 63%
rename from lib/spout/src/Spout/Common/Entity/Style/Border.php
rename to lib/openspout/src/Common/Entity/Style/Border.php
index bbf43ad30263e..53b59dab756f3 100644
--- a/lib/spout/src/Spout/Common/Entity/Style/Border.php
+++ b/lib/openspout/src/Common/Entity/Style/Border.php
@@ -1,33 +1,27 @@
setParts($borderParts);
@@ -35,7 +29,8 @@ public function __construct(array $borderParts = [])
/**
* @param string $name The name of the border part
- * @return BorderPart|null
+ *
+ * @return null|BorderPart
*/
public function getPart($name)
{
@@ -44,6 +39,7 @@ public function getPart($name)
/**
* @param string $name The name of the border part
+ *
* @return bool
*/
public function hasPart($name)
@@ -60,20 +56,19 @@ public function getParts()
}
/**
- * Set BorderParts
+ * Set BorderParts.
+ *
* @param array $parts
- * @return void
*/
public function setParts($parts)
{
- unset($this->parts);
+ $this->parts = [];
foreach ($parts as $part) {
$this->addPart($part);
}
}
/**
- * @param BorderPart $borderPart
* @return Border
*/
public function addPart(BorderPart $borderPart)
diff --git a/lib/spout/src/Spout/Common/Entity/Style/BorderPart.php b/lib/openspout/src/Common/Entity/Style/BorderPart.php
similarity index 76%
rename from lib/spout/src/Spout/Common/Entity/Style/BorderPart.php
rename to lib/openspout/src/Common/Entity/Style/BorderPart.php
index c0874847e6214..afee7767fc3d2 100644
--- a/lib/spout/src/Spout/Common/Entity/Style/BorderPart.php
+++ b/lib/openspout/src/Common/Entity/Style/BorderPart.php
@@ -1,38 +1,35 @@
name = $name;
@@ -109,12 +107,12 @@ public function getStyle()
/**
* @param string $style The style of the border part @see BorderPart::$allowedStyles
+ *
* @throws InvalidStyleException
- * @return void
*/
public function setStyle($style)
{
- if (!\in_array($style, self::$allowedStyles)) {
+ if (!\in_array($style, self::$allowedStyles, true)) {
throw new InvalidStyleException($style);
}
$this->style = $style;
@@ -130,7 +128,6 @@ public function getColor()
/**
* @param string $color The color of the border part @see Color::rgb()
- * @return void
*/
public function setColor($color)
{
@@ -147,12 +144,12 @@ public function getWidth()
/**
* @param string $width The width of the border part @see BorderPart::$allowedWidths
+ *
* @throws InvalidWidthException
- * @return void
*/
public function setWidth($width)
{
- if (!\in_array($width, self::$allowedWidths)) {
+ if (!\in_array($width, self::$allowedWidths, true)) {
throw new InvalidWidthException($width);
}
$this->width = $width;
diff --git a/lib/spout/src/Spout/Common/Entity/Style/CellAlignment.php b/lib/openspout/src/Common/Entity/Style/CellAlignment.php
similarity index 73%
rename from lib/spout/src/Spout/Common/Entity/Style/CellAlignment.php
rename to lib/openspout/src/Common/Entity/Style/CellAlignment.php
index 60fe83309fb3d..ecfa6d7f72878 100644
--- a/lib/spout/src/Spout/Common/Entity/Style/CellAlignment.php
+++ b/lib/openspout/src/Common/Entity/Style/CellAlignment.php
@@ -1,17 +1,16 @@
1,
diff --git a/lib/spout/src/Spout/Common/Entity/Style/Color.php b/lib/openspout/src/Common/Entity/Style/Color.php
similarity index 58%
rename from lib/spout/src/Spout/Common/Entity/Style/Color.php
rename to lib/openspout/src/Common/Entity/Style/Color.php
index f2a4731a0cd52..cd9bdfd5cfb86 100644
--- a/lib/spout/src/Spout/Common/Entity/Style/Color.php
+++ b/lib/openspout/src/Common/Entity/Style/Color.php
@@ -1,35 +1,35 @@
255) {
- throw new InvalidColorException("The RGB components must be between 0 and 255. Received: $colorComponent");
+ throw new InvalidColorException("The RGB components must be between 0 and 255. Received: {$colorComponent}");
}
}
/**
- * Converts the color component to its corresponding hexadecimal value
+ * Converts the color component to its corresponding hexadecimal value.
*
* @param int $colorComponent Color component, 0 - 255
+ *
* @return string Corresponding hexadecimal value, with a leading 0 if needed. E.g "0f", "2d"
*/
protected static function convertColorComponentToHex($colorComponent)
{
- return \str_pad(\dechex($colorComponent), 2, '0', STR_PAD_LEFT);
- }
-
- /**
- * Returns the ARGB color of the given RGB color,
- * assuming that alpha value is always 1.
- *
- * @param string $rgbColor RGB color like "FF08B2"
- * @return string ARGB color
- */
- public static function toARGB($rgbColor)
- {
- return 'FF' . $rgbColor;
+ return str_pad(dechex($colorComponent), 2, '0', STR_PAD_LEFT);
}
}
diff --git a/lib/spout/src/Spout/Common/Entity/Style/Style.php b/lib/openspout/src/Common/Entity/Style/Style.php
similarity index 86%
rename from lib/spout/src/Spout/Common/Entity/Style/Style.php
rename to lib/openspout/src/Common/Entity/Style/Style.php
index 2bacc7afa8f71..28f1597050af2 100644
--- a/lib/spout/src/Spout/Common/Entity/Style/Style.php
+++ b/lib/openspout/src/Common/Entity/Style/Style.php
@@ -1,19 +1,18 @@
hasSetFormat;
}
- /**
- * @return bool
- */
- public function isRegistered() : bool
+ public function isRegistered(): bool
{
return $this->isRegistered;
}
- public function markAsRegistered(?int $id) : void
+ public function markAsRegistered(?int $id): void
{
$this->setId($id);
$this->isRegistered = true;
}
- public function unmarkAsRegistered() : void
+ public function unmarkAsRegistered(): void
{
$this->setId(0);
$this->isRegistered = false;
}
- public function isEmpty() : bool
+ public function isEmpty(): bool
{
return $this->isEmpty;
}
+
+ /**
+ * Sets should shrink to fit.
+ *
+ * @param bool $shrinkToFit
+ *
+ * @return Style
+ */
+ public function setShouldShrinkToFit($shrinkToFit = true)
+ {
+ $this->hasSetShrinkToFit = true;
+ $this->shouldShrinkToFit = $shrinkToFit;
+
+ return $this;
+ }
+
+ /**
+ * @return bool Whether format should be applied
+ */
+ public function shouldShrinkToFit()
+ {
+ return $this->shouldShrinkToFit;
+ }
+
+ /**
+ * @return bool
+ */
+ public function hasSetShrinkToFit()
+ {
+ return $this->hasSetShrinkToFit;
+ }
}
diff --git a/lib/openspout/src/Common/Exception/EncodingConversionException.php b/lib/openspout/src/Common/Exception/EncodingConversionException.php
new file mode 100644
index 0000000000000..ef0cdc6d80b10
--- /dev/null
+++ b/lib/openspout/src/Common/Exception/EncodingConversionException.php
@@ -0,0 +1,7 @@
+globalFunctionsHelper = $globalFunctionsHelper;
$this->supportedEncodingsWithBom = [
- self::ENCODING_UTF8 => self::BOM_UTF8,
+ self::ENCODING_UTF8 => self::BOM_UTF8,
self::ENCODING_UTF16_LE => self::BOM_UTF16_LE,
self::ENCODING_UTF16_BE => self::BOM_UTF16_BE,
self::ENCODING_UTF32_LE => self::BOM_UTF32_LE,
@@ -50,7 +49,8 @@ public function __construct($globalFunctionsHelper)
* Returns the number of bytes to use as offset in order to skip the BOM.
*
* @param resource $filePointer Pointer to the file to check
- * @param string $encoding Encoding of the file to check
+ * @param string $encoding Encoding of the file to check
+ *
* @return int Bytes offset to apply to skip the BOM (0 means no BOM)
*/
public function getBytesOffsetToSkipBOM($filePointer, $encoding)
@@ -67,35 +67,14 @@ public function getBytesOffsetToSkipBOM($filePointer, $encoding)
return $byteOffsetToSkipBom;
}
- /**
- * Returns whether the file identified by the given pointer has a BOM.
- *
- * @param resource $filePointer Pointer to the file to check
- * @param string $encoding Encoding of the file to check
- * @return bool TRUE if the file has a BOM, FALSE otherwise
- */
- protected function hasBOM($filePointer, $encoding)
- {
- $hasBOM = false;
-
- $this->globalFunctionsHelper->rewind($filePointer);
-
- if (\array_key_exists($encoding, $this->supportedEncodingsWithBom)) {
- $potentialBom = $this->supportedEncodingsWithBom[$encoding];
- $numBytesInBom = \strlen($potentialBom);
-
- $hasBOM = ($this->globalFunctionsHelper->fgets($filePointer, $numBytesInBom + 1) === $potentialBom);
- }
-
- return $hasBOM;
- }
-
/**
* Attempts to convert a non UTF-8 string into UTF-8.
*
- * @param string $string Non UTF-8 string to be converted
+ * @param string $string Non UTF-8 string to be converted
* @param string $sourceEncoding The encoding used to encode the source string
- * @throws \Box\Spout\Common\Exception\EncodingConversionException If conversion is not supported or if the conversion failed
+ *
+ * @throws \OpenSpout\Common\Exception\EncodingConversionException If conversion is not supported or if the conversion failed
+ *
* @return string The converted, UTF-8 string
*/
public function attemptConversionToUTF8($string, $sourceEncoding)
@@ -106,9 +85,11 @@ public function attemptConversionToUTF8($string, $sourceEncoding)
/**
* Attempts to convert a UTF-8 string into the given encoding.
*
- * @param string $string UTF-8 string to be converted
+ * @param string $string UTF-8 string to be converted
* @param string $targetEncoding The encoding the string should be re-encoded into
- * @throws \Box\Spout\Common\Exception\EncodingConversionException If conversion is not supported or if the conversion failed
+ *
+ * @throws \OpenSpout\Common\Exception\EncodingConversionException If conversion is not supported or if the conversion failed
+ *
* @return string The converted string, encoded with the given encoding
*/
public function attemptConversionFromUTF8($string, $targetEncoding)
@@ -116,14 +97,40 @@ public function attemptConversionFromUTF8($string, $targetEncoding)
return $this->attemptConversion($string, self::ENCODING_UTF8, $targetEncoding);
}
+ /**
+ * Returns whether the file identified by the given pointer has a BOM.
+ *
+ * @param resource $filePointer Pointer to the file to check
+ * @param string $encoding Encoding of the file to check
+ *
+ * @return bool TRUE if the file has a BOM, FALSE otherwise
+ */
+ protected function hasBOM($filePointer, $encoding)
+ {
+ $hasBOM = false;
+
+ $this->globalFunctionsHelper->rewind($filePointer);
+
+ if (\array_key_exists($encoding, $this->supportedEncodingsWithBom)) {
+ $potentialBom = $this->supportedEncodingsWithBom[$encoding];
+ $numBytesInBom = \strlen($potentialBom);
+
+ $hasBOM = ($this->globalFunctionsHelper->fgets($filePointer, $numBytesInBom + 1) === $potentialBom);
+ }
+
+ return $hasBOM;
+ }
+
/**
* Attempts to convert the given string to the given encoding.
* Depending on what is installed on the server, we will try to iconv or mbstring.
*
- * @param string $string string to be converted
+ * @param string $string string to be converted
* @param string $sourceEncoding The encoding used to encode the source string
* @param string $targetEncoding The encoding the string should be re-encoded into
- * @throws \Box\Spout\Common\Exception\EncodingConversionException If conversion is not supported or if the conversion failed
+ *
+ * @throws \OpenSpout\Common\Exception\EncodingConversionException If conversion is not supported or if the conversion failed
+ *
* @return string The converted string, encoded with the given encoding
*/
protected function attemptConversion($string, $sourceEncoding, $targetEncoding)
@@ -140,11 +147,11 @@ protected function attemptConversion($string, $sourceEncoding, $targetEncoding)
} elseif ($this->canUseMbString()) {
$convertedString = $this->globalFunctionsHelper->mb_convert_encoding($string, $sourceEncoding, $targetEncoding);
} else {
- throw new EncodingConversionException("The conversion from $sourceEncoding to $targetEncoding is not supported. Please install \"iconv\" or \"PHP Intl\".");
+ throw new EncodingConversionException("The conversion from {$sourceEncoding} to {$targetEncoding} is not supported. Please install \"iconv\" or \"PHP Intl\".");
}
- if ($convertedString === false) {
- throw new EncodingConversionException("The conversion from $sourceEncoding to $targetEncoding failed.");
+ if (false === $convertedString) {
+ throw new EncodingConversionException("The conversion from {$sourceEncoding} to {$targetEncoding} failed.");
}
return $convertedString;
diff --git a/lib/spout/src/Spout/Common/Helper/Escaper/CSV.php b/lib/openspout/src/Common/Helper/Escaper/CSV.php
similarity index 75%
rename from lib/spout/src/Spout/Common/Helper/Escaper/CSV.php
rename to lib/openspout/src/Common/Helper/Escaper/CSV.php
index 26777e8d9ac51..d68199af89cf8 100644
--- a/lib/spout/src/Spout/Common/Helper/Escaper/CSV.php
+++ b/lib/openspout/src/Common/Helper/Escaper/CSV.php
@@ -1,19 +1,19 @@
', '&') 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
- // @link https://github.com/box/spout/issues/329
- $replacedString = \htmlspecialchars($string, ENT_QUOTES | ENT_DISALLOWED, 'UTF-8');
+ /**
+ * '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');
+ $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' .
+ $regexPattern = '[\x00-\x08'.
// skipping "\t" (0x9) and "\n" (0xA)
- '\x0B-\x0C' .
+ '\x0B-\x0C'.
// skipping "\r" (0xD)
'\x0E-\x1F]';
- $replacedString = \preg_replace("/$regexPattern/", '�', $escapedString);
+ $replacedString = preg_replace("/{$regexPattern}/", '�', $escapedString);
}
return $replacedString;
}
/**
- * Unescapes the given string to make it compatible with XLSX
+ * 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)
diff --git a/lib/spout/src/Spout/Common/Helper/Escaper/XLSX.php b/lib/openspout/src/Common/Helper/Escaper/XLSX.php
similarity index 80%
rename from lib/spout/src/Spout/Common/Helper/Escaper/XLSX.php
rename to lib/openspout/src/Common/Helper/Escaper/XLSX.php
index daef46e4dd26f..16bb162a38f7b 100644
--- a/lib/spout/src/Spout/Common/Helper/Escaper/XLSX.php
+++ b/lib/openspout/src/Common/Helper/Escaper/XLSX.php
@@ -1,10 +1,9 @@
isAlreadyInitialized) {
- $this->escapableControlCharactersPattern = $this->getEscapableControlCharactersPattern();
- $this->controlCharactersEscapingMap = $this->getControlCharactersEscapingMap();
- $this->controlCharactersEscapingReverseMap = \array_flip($this->controlCharactersEscapingMap);
-
- $this->isAlreadyInitialized = true;
- }
- }
-
- /**
- * Escapes the given string to make it compatible with XLSX
+ * 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)
@@ -47,15 +33,14 @@ public function escape($string)
$escapedString = $this->escapeControlCharacters($string);
// @NOTE: Using ENT_QUOTES as XML entities ('<', '>', '&') as well as
// single/double quotes (for XML attributes) need to be encoded.
- $escapedString = \htmlspecialchars($escapedString, ENT_QUOTES, 'UTF-8');
-
- return $escapedString;
+ return htmlspecialchars($escapedString, ENT_QUOTES, 'UTF-8');
}
/**
- * Unescapes the given string to make it compatible with XLSX
+ * 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)
@@ -68,9 +53,21 @@ public function unescape($string)
// 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()".
- $unescapedString = $this->unescapeControlCharacters($string);
+ return $this->unescapeControlCharacters($string);
+ }
- return $unescapedString;
+ /**
+ * Initializes the control characters if not already done.
+ */
+ protected function initIfNeeded()
+ {
+ if (!$this->isAlreadyInitialized) {
+ $this->escapableControlCharactersPattern = $this->getEscapableControlCharactersPattern();
+ $this->controlCharactersEscapingMap = $this->getControlCharactersEscapingMap();
+ $this->controlCharactersEscapingReverseMap = array_flip($this->controlCharactersEscapingMap);
+
+ $this->isAlreadyInitialized = true;
+ }
}
/**
@@ -80,9 +77,9 @@ protected function getEscapableControlCharactersPattern()
{
// 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".
- return '[\x00-\x08' .
+ return '[\x00-\x08'.
// skipping "\t" (0x9) and "\n" (0xA)
- '\x0B-\x0C' .
+ '\x0B-\x0C'.
// skipping "\r" (0xD)
'\x0E-\x1F]';
}
@@ -93,6 +90,7 @@ protected function getEscapableControlCharactersPattern()
* "\t", "\r" and "\n" don't need to be escaped.
*
* NOTE: the logic has been adapted from the XlsxWriter library (BSD License)
+ *
* @see https://github.com/jmcnamara/XlsxWriter/blob/f1e610f29/xlsxwriter/sharedstrings.py#L89
*
* @return string[]
@@ -102,11 +100,11 @@ protected function getControlCharactersEscapingMap()
$controlCharactersEscapingMap = [];
// control characters values are from 0 to 1F (hex values) in the ASCII table
- for ($charValue = 0x00; $charValue <= 0x1F; $charValue++) {
+ for ($charValue = 0x00; $charValue <= 0x1F; ++$charValue) {
$character = \chr($charValue);
- if (\preg_match("/{$this->escapableControlCharactersPattern}/", $character)) {
- $charHexValue = \dechex($charValue);
- $escapedChar = '_x' . \sprintf('%04s', \strtoupper($charHexValue)) . '_';
+ if (preg_match("/{$this->escapableControlCharactersPattern}/", $character)) {
+ $charHexValue = dechex($charValue);
+ $escapedChar = '_x'.sprintf('%04s', strtoupper($charHexValue)).'_';
$controlCharactersEscapingMap[$escapedChar] = $character;
}
}
@@ -115,16 +113,18 @@ protected function getControlCharactersEscapingMap()
}
/**
- * Converts PHP control characters from the given string to OpenXML escaped control characters
+ * Converts PHP control characters from the given string to OpenXML escaped control characters.
*
* Excel escapes control characters with _xHHHH_ and also escapes any
* literal strings of that type by encoding the leading underscore.
* So "\0" -> _x0000_ and "_x0000_" -> _x005F_x0000_.
*
* NOTE: the logic has been adapted from the XlsxWriter library (BSD License)
+ *
* @see https://github.com/jmcnamara/XlsxWriter/blob/f1e610f29/xlsxwriter/sharedstrings.py#L89
*
* @param string $string String to escape
+ *
* @return string
*/
protected function escapeControlCharacters($string)
@@ -132,37 +132,40 @@ protected function escapeControlCharacters($string)
$escapedString = $this->escapeEscapeCharacter($string);
// if no control characters
- if (!\preg_match("/{$this->escapableControlCharactersPattern}/", $escapedString)) {
+ if (!preg_match("/{$this->escapableControlCharactersPattern}/", $escapedString)) {
return $escapedString;
}
- return \preg_replace_callback("/({$this->escapableControlCharactersPattern})/", function ($matches) {
+ return preg_replace_callback("/({$this->escapableControlCharactersPattern})/", function ($matches) {
return $this->controlCharactersEscapingReverseMap[$matches[0]];
}, $escapedString);
}
/**
- * Escapes the escape character: "_x0000_" -> "_x005F_x0000_"
+ * Escapes the escape character: "_x0000_" -> "_x005F_x0000_".
*
* @param string $string String to escape
+ *
* @return string The escaped string
*/
protected function escapeEscapeCharacter($string)
{
- return \preg_replace('/_(x[\dA-F]{4})_/', '_x005F_$1_', $string);
+ return preg_replace('/_(x[\dA-F]{4})_/', '_x005F_$1_', $string);
}
/**
- * Converts OpenXML escaped control characters from the given string to PHP control characters
+ * Converts OpenXML escaped control characters from the given string to PHP control characters.
*
* Excel escapes control characters with _xHHHH_ and also escapes any
* literal strings of that type by encoding the leading underscore.
* So "_x0000_" -> "\0" and "_x005F_x0000_" -> "_x0000_"
*
* NOTE: the logic has been adapted from the XlsxWriter library (BSD License)
+ *
* @see https://github.com/jmcnamara/XlsxWriter/blob/f1e610f29/xlsxwriter/sharedstrings.py#L89
*
* @param string $string String to unescape
+ *
* @return string
*/
protected function unescapeControlCharacters($string)
@@ -171,20 +174,21 @@ protected function unescapeControlCharacters($string)
foreach ($this->controlCharactersEscapingMap as $escapedCharValue => $charValue) {
// only unescape characters that don't contain the escaped escape character for now
- $unescapedString = \preg_replace("/(?unescapeEscapeCharacter($unescapedString);
}
/**
- * Unecapes the escape character: "_x005F_x0000_" => "_x0000_"
+ * Unecapes the escape character: "_x005F_x0000_" => "_x0000_".
*
* @param string $string String to unescape
+ *
* @return string The unescaped string
*/
protected function unescapeEscapeCharacter($string)
{
- return \preg_replace('/_x005F(_x[\dA-F]{4}_)/', '$1', $string);
+ return preg_replace('/_x005F(_x[\dA-F]{4}_)/', '$1', $string);
}
}
diff --git a/lib/spout/src/Spout/Common/Helper/FileSystemHelper.php b/lib/openspout/src/Common/Helper/FileSystemHelper.php
similarity index 65%
rename from lib/spout/src/Spout/Common/Helper/FileSystemHelper.php
rename to lib/openspout/src/Common/Helper/FileSystemHelper.php
index 4d21fd38c1469..130f1f86e4738 100644
--- a/lib/spout/src/Spout/Common/Helper/FileSystemHelper.php
+++ b/lib/openspout/src/Common/Helper/FileSystemHelper.php
@@ -1,13 +1,12 @@
baseFolderRealPath = \realpath($baseFolderPath);
+ $this->baseFolderRealPath = realpath($baseFolderPath);
}
/**
* Creates an empty folder with the given name under the given parent folder.
*
* @param string $parentFolderPath The parent folder path under which the folder is going to be created
- * @param string $folderName The name of the folder to create
- * @throws \Box\Spout\Common\Exception\IOException If unable to create the folder or if the folder path is not inside of the base folder
+ * @param string $folderName The name of the folder to create
+ *
+ * @throws \OpenSpout\Common\Exception\IOException If unable to create the folder or if the folder path is not inside of the base folder
+ *
* @return string Path of the created folder
*/
public function createFolder($parentFolderPath, $folderName)
{
$this->throwIfOperationNotInBaseFolder($parentFolderPath);
- $folderPath = $parentFolderPath . '/' . $folderName;
+ $folderPath = $parentFolderPath.'/'.$folderName;
- $wasCreationSuccessful = \mkdir($folderPath, 0777, true);
+ $wasCreationSuccessful = mkdir($folderPath, 0777, true);
if (!$wasCreationSuccessful) {
- throw new IOException("Unable to create folder: $folderPath");
+ throw new IOException("Unable to create folder: {$folderPath}");
}
return $folderPath;
@@ -49,47 +50,49 @@ public function createFolder($parentFolderPath, $folderName)
* The parent folder must exist.
*
* @param string $parentFolderPath The parent folder path where the file is going to be created
- * @param string $fileName The name of the file to create
- * @param string $fileContents The contents of the file to create
- * @throws \Box\Spout\Common\Exception\IOException If unable to create the file or if the file path is not inside of the base folder
+ * @param string $fileName The name of the file to create
+ * @param string $fileContents The contents of the file to create
+ *
+ * @throws \OpenSpout\Common\Exception\IOException If unable to create the file or if the file path is not inside of the base folder
+ *
* @return string Path of the created file
*/
public function createFileWithContents($parentFolderPath, $fileName, $fileContents)
{
$this->throwIfOperationNotInBaseFolder($parentFolderPath);
- $filePath = $parentFolderPath . '/' . $fileName;
+ $filePath = $parentFolderPath.'/'.$fileName;
- $wasCreationSuccessful = \file_put_contents($filePath, $fileContents);
- if ($wasCreationSuccessful === false) {
- throw new IOException("Unable to create file: $filePath");
+ $wasCreationSuccessful = file_put_contents($filePath, $fileContents);
+ if (false === $wasCreationSuccessful) {
+ throw new IOException("Unable to create file: {$filePath}");
}
return $filePath;
}
/**
- * Delete the file at the given path
+ * Delete the file at the given path.
*
* @param string $filePath Path of the file to delete
- * @throws \Box\Spout\Common\Exception\IOException If the file path is not inside of the base folder
- * @return void
+ *
+ * @throws \OpenSpout\Common\Exception\IOException If the file path is not inside of the base folder
*/
public function deleteFile($filePath)
{
$this->throwIfOperationNotInBaseFolder($filePath);
- if (\file_exists($filePath) && \is_file($filePath)) {
- \unlink($filePath);
+ if (file_exists($filePath) && is_file($filePath)) {
+ unlink($filePath);
}
}
/**
- * Delete the folder at the given path as well as all its contents
+ * Delete the folder at the given path as well as all its contents.
*
* @param string $folderPath Path of the folder to delete
- * @throws \Box\Spout\Common\Exception\IOException If the folder path is not inside of the base folder
- * @return void
+ *
+ * @throws \OpenSpout\Common\Exception\IOException If the folder path is not inside of the base folder
*/
public function deleteFolderRecursively($folderPath)
{
@@ -102,13 +105,13 @@ public function deleteFolderRecursively($folderPath)
foreach ($itemIterator as $item) {
if ($item->isDir()) {
- \rmdir($item->getPathname());
+ rmdir($item->getPathname());
} else {
- \unlink($item->getPathname());
+ unlink($item->getPathname());
}
}
- \rmdir($folderPath);
+ rmdir($folderPath);
}
/**
@@ -117,17 +120,17 @@ public function deleteFolderRecursively($folderPath)
* should occur is not inside the base folder.
*
* @param string $operationFolderPath The path of the folder where the I/O operation should occur
- * @throws \Box\Spout\Common\Exception\IOException If the folder where the I/O operation should occur
- * is not inside the base folder or the base folder does not exist
- * @return void
+ *
+ * @throws \OpenSpout\Common\Exception\IOException If the folder where the I/O operation should occur
+ * is not inside the base folder or the base folder does not exist
*/
protected function throwIfOperationNotInBaseFolder(string $operationFolderPath)
{
- $operationFolderRealPath = \realpath($operationFolderPath);
+ $operationFolderRealPath = realpath($operationFolderPath);
if (!$this->baseFolderRealPath) {
throw new IOException("The base folder path is invalid: {$this->baseFolderRealPath}");
}
- $isInBaseFolder = (\strpos($operationFolderRealPath, $this->baseFolderRealPath) === 0);
+ $isInBaseFolder = (0 === strpos($operationFolderRealPath, $this->baseFolderRealPath));
if (!$isInBaseFolder) {
throw new IOException("Cannot perform I/O operation outside of the base folder: {$this->baseFolderRealPath}");
}
diff --git a/lib/spout/src/Spout/Common/Helper/FileSystemHelperInterface.php b/lib/openspout/src/Common/Helper/FileSystemHelperInterface.php
similarity index 68%
rename from lib/spout/src/Spout/Common/Helper/FileSystemHelperInterface.php
rename to lib/openspout/src/Common/Helper/FileSystemHelperInterface.php
index 53c7dd9e1e106..90082d28bf92f 100644
--- a/lib/spout/src/Spout/Common/Helper/FileSystemHelperInterface.php
+++ b/lib/openspout/src/Common/Helper/FileSystemHelperInterface.php
@@ -1,11 +1,10 @@
= 70400 ? '' : "\0";
+ /**
+ * 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);
+ return fgetcsv($handle, $length, $delimiter, $enclosure, $escapeCharacter);
}
/**
- * Wrapper around global function fputcsv()
+ * Wrapper around global function fputcsv().
+ *
* @see fputcsv()
*
- * @param resource $handle
- * @param array $fields
- * @param string|null $delimiter
- * @param string|null $enclosure
- * @return int
+ * @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";
+ /**
+ * 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);
+ return fputcsv($handle, $fields, $delimiter, $enclosure, $escapeCharacter);
}
/**
- * Wrapper around global function fwrite()
+ * Wrapper around global function fwrite().
+ *
* @see fwrite()
*
* @param resource $handle
- * @param string $string
+ * @param string $string
+ *
* @return int
*/
public function fwrite($handle, $string)
{
- return \fwrite($handle, $string);
+ return fwrite($handle, $string);
}
/**
- * Wrapper around global function fclose()
+ * Wrapper around global function fclose().
+ *
* @see fclose()
*
* @param resource $handle
+ *
* @return bool
*/
public function fclose($handle)
{
- return \fclose($handle);
+ return fclose($handle);
}
/**
- * Wrapper around global function rewind()
+ * Wrapper around global function rewind().
+ *
* @see rewind()
*
* @param resource $handle
+ *
* @return bool
*/
public function rewind($handle)
{
- return \rewind($handle);
+ return rewind($handle);
}
/**
- * Wrapper around global function file_exists()
+ * Wrapper around global function file_exists().
+ *
* @see file_exists()
*
* @param string $fileName
+ *
* @return bool
*/
public function file_exists($fileName)
{
- return \file_exists($fileName);
+ return file_exists($fileName);
}
/**
- * Wrapper around global function file_get_contents()
+ * 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);
+ return file_get_contents($realFilePath);
}
/**
- * Updates the given file path to use a real path.
- * This is to avoid issues on some Windows setup.
+ * Wrapper around global function feof().
*
- * @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 (\strpos($path, 'zip://') === 0);
- }
-
- /**
- * Wrapper around global function feof()
* @see feof()
*
* @param resource $handle
+ *
* @return bool
*/
public function feof($handle)
{
- return \feof($handle);
+ return feof($handle);
}
/**
- * Wrapper around global function is_readable()
+ * Wrapper around global function is_readable().
+ *
* @see is_readable()
*
* @param string $fileName
+ *
* @return bool
*/
public function is_readable($fileName)
{
- return \is_readable($fileName);
+ return is_readable($fileName);
}
/**
- * Wrapper around global function basename()
+ * Wrapper around global function basename().
+ *
* @see basename()
*
* @param string $path
* @param string $suffix
+ *
* @return string
*/
public function basename($path, $suffix = '')
{
- return \basename($path, $suffix);
+ return basename($path, $suffix);
}
/**
- * Wrapper around global function header()
+ * Wrapper around global function header().
+ *
* @see header()
*
* @param string $string
- * @return void
*/
public function header($string)
{
- \header($string);
+ header($string);
}
/**
- * Wrapper around global function ob_end_clean()
- * @see ob_end_clean()
+ * Wrapper around global function ob_end_clean().
*
- * @return void
+ * @see ob_end_clean()
*/
public function ob_end_clean()
{
- if (\ob_get_length() > 0) {
- \ob_end_clean();
+ if (ob_get_length() > 0) {
+ ob_end_clean();
}
}
/**
- * Wrapper around global function iconv()
+ * Wrapper around global function iconv().
+ *
* @see iconv()
*
- * @param string $string The string to be converted
+ * @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 string|bool the converted string or FALSE on failure.
+ *
+ * @return bool|string the converted string or FALSE on failure
*/
public function iconv($string, $sourceEncoding, $targetEncoding)
{
- return \iconv($sourceEncoding, $targetEncoding, $string);
+ return iconv($sourceEncoding, $targetEncoding, $string);
}
/**
- * Wrapper around global function mb_convert_encoding()
+ * Wrapper around global function mb_convert_encoding().
+ *
* @see mb_convert_encoding()
*
- * @param string $string The string to be converted
+ * @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 string|bool the converted string or FALSE on failure.
+ *
+ * @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);
+ return mb_convert_encoding($string, $targetEncoding, $sourceEncoding);
}
/**
- * Wrapper around global function stream_get_wrappers()
+ * Wrapper around global function stream_get_wrappers().
+ *
* @see stream_get_wrappers()
*
* @return array
*/
public function stream_get_wrappers()
{
- return \stream_get_wrappers();
+ return stream_get_wrappers();
}
/**
- * Wrapper around global function function_exists()
+ * 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://');
+ }
}
diff --git a/lib/spout/src/Spout/Common/Helper/StringHelper.php b/lib/openspout/src/Common/Helper/StringHelper.php
similarity index 76%
rename from lib/spout/src/Spout/Common/Helper/StringHelper.php
rename to lib/openspout/src/Common/Helper/StringHelper.php
index 6256b1e5f3fca..96e7f2c3a1cfd 100644
--- a/lib/spout/src/Spout/Common/Helper/StringHelper.php
+++ b/lib/openspout/src/Common/Helper/StringHelper.php
@@ -1,9 +1,8 @@
hasMbstringSupport = \extension_loaded('mbstring');
- $this->isRunningPhp7OrOlder = \version_compare(PHP_VERSION, '8.0.0') < 0;
- $this->localeInfo = \localeconv();
+ $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);
+ 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 $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);
+ $position = $this->hasMbstringSupport ? mb_strpos($string, $char) : strpos($string, $char);
- return ($position !== false) ? $position : -1;
+ 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 $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);
+ $position = $this->hasMbstringSupport ? mb_strrpos($string, $char) : strrpos($string, $char);
- return ($position !== false) ? $position : -1;
+ return (false !== $position) ? $position : -1;
}
/**
@@ -87,16 +89,17 @@ public function getCharLastOccurrencePosition($char, $string)
*
* @see https://wiki.php.net/rfc/locale_independent_float_to_string for the changed behavior in PHP8.
*
- * @param int|float $numericValue
- * @return string
+ * @param float|int $numericValue
+ *
+ * @return float|int|string
*/
public function formatNumericValue($numericValue)
{
- if ($this->isRunningPhp7OrOlder && is_float($numericValue)) {
+ if ($this->isRunningPhp7OrOlder && \is_float($numericValue)) {
return str_replace(
[$this->localeInfo['thousands_sep'], $this->localeInfo['decimal_point']],
['', '.'],
- $numericValue
+ (string) $numericValue
);
}
diff --git a/lib/spout/src/Spout/Common/Manager/OptionsManagerAbstract.php b/lib/openspout/src/Common/Manager/OptionsManagerAbstract.php
similarity index 58%
rename from lib/spout/src/Spout/Common/Manager/OptionsManagerAbstract.php
rename to lib/openspout/src/Common/Manager/OptionsManagerAbstract.php
index 5670b95be56ca..3bfce395ccf85 100644
--- a/lib/spout/src/Spout/Common/Manager/OptionsManagerAbstract.php
+++ b/lib/openspout/src/Common/Manager/OptionsManagerAbstract.php
@@ -1,13 +1,10 @@
supportedOptions, true)) {
+ $this->options[$optionName] = $optionValue;
+ }
+ }
/**
- * Sets the given option, if this option is supported.
+ * Add an option to the internal list of options
+ * Used only for mergeCells() for now.
*
- * @param string $optionName
+ * @param mixed $optionName
* @param mixed $optionValue
- * @return void
*/
- public function setOption($optionName, $optionValue)
+ public function addOption($optionName, $optionValue)
{
- if (\in_array($optionName, $this->supportedOptions)) {
- $this->options[$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 mixed|null The set option or NULL if no option with given name found
+ *
+ * @return null|mixed The set option or NULL if no option with given name found
*/
public function getOption($optionName)
{
@@ -65,4 +68,15 @@ public function getOption($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();
}
diff --git a/lib/openspout/src/Common/Manager/OptionsManagerInterface.php b/lib/openspout/src/Common/Manager/OptionsManagerInterface.php
new file mode 100644
index 0000000000000..7913017a50c8d
--- /dev/null
+++ b/lib/openspout/src/Common/Manager/OptionsManagerInterface.php
@@ -0,0 +1,31 @@
+helperFactory = $helperFactory;
}
/**
- * @param resource $filePointer Pointer to the CSV file to read
+ * @param resource $filePointer Pointer to the CSV file to read
* @param OptionsManagerInterface $optionsManager
- * @param GlobalFunctionsHelper $globalFunctionsHelper
+ * @param GlobalFunctionsHelper $globalFunctionsHelper
+ *
* @return SheetIterator
*/
public function createSheetIterator($filePointer, $optionsManager, $globalFunctionsHelper)
@@ -43,30 +40,9 @@ public function createSheetIterator($filePointer, $optionsManager, $globalFuncti
return new SheetIterator($sheet);
}
- /**
- * @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);
- }
-
/**
* @param Cell[] $cells
+ *
* @return Row
*/
public function createRow(array $cells = [])
@@ -76,6 +52,7 @@ public function createRow(array $cells = [])
/**
* @param mixed $cellValue
+ *
* @return Cell
*/
public function createCell($cellValue)
@@ -84,15 +61,38 @@ public function createCell($cellValue)
}
/**
- * @param array $cellValues
* @return Row
*/
public function createRowFromArray(array $cellValues = [])
{
- $cells = \array_map(function ($cellValue) {
+ $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);
+ }
}
diff --git a/lib/spout/src/Spout/Reader/CSV/Manager/OptionsManager.php b/lib/openspout/src/Reader/CSV/Manager/OptionsManager.php
similarity index 77%
rename from lib/spout/src/Spout/Reader/CSV/Manager/OptionsManager.php
rename to lib/openspout/src/Reader/CSV/Manager/OptionsManager.php
index befefe35065c8..9772a43815cfb 100644
--- a/lib/spout/src/Spout/Reader/CSV/Manager/OptionsManager.php
+++ b/lib/openspout/src/Reader/CSV/Manager/OptionsManager.php
@@ -1,14 +1,13 @@
= 8.1 */
private $isRunningAtLeastPhp81;
- /**
- * @param OptionsManagerInterface $optionsManager
- * @param GlobalFunctionsHelper $globalFunctionsHelper
- * @param InternalEntityFactoryInterface $entityFactory
- */
public function __construct(
OptionsManagerInterface $optionsManager,
GlobalFunctionsHelper $globalFunctionsHelper,
InternalEntityFactoryInterface $entityFactory
) {
parent::__construct($optionsManager, $globalFunctionsHelper, $entityFactory);
- $this->isRunningAtLeastPhp81 = \version_compare(PHP_VERSION, '8.1.0') >= 0;
+ $this->isRunningAtLeastPhp81 = version_compare(PHP_VERSION, '8.1.0') >= 0;
}
/**
@@ -47,6 +41,7 @@ public function __construct(
* Needs to be called before opening the reader.
*
* @param string $fieldDelimiter Character that delimits fields
+ *
* @return Reader
*/
public function setFieldDelimiter($fieldDelimiter)
@@ -61,6 +56,7 @@ public function setFieldDelimiter($fieldDelimiter)
* Needs to be called before opening the reader.
*
* @param string $fieldEnclosure Character that enclose fields
+ *
* @return Reader
*/
public function setFieldEnclosure($fieldEnclosure)
@@ -75,6 +71,7 @@ public function setFieldEnclosure($fieldEnclosure)
* 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)
@@ -85,7 +82,7 @@ public function setEncoding($encoding)
}
/**
- * Returns whether stream wrappers are supported
+ * Returns whether stream wrappers are supported.
*
* @return bool
*/
@@ -98,21 +95,21 @@ protected function doesSupportStreamWrapper()
* 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 \Box\Spout\Common\Exception\IOException
- * @return void
+ * @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->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.");
+ throw new IOException("Could not open file {$filePath} for reading.");
}
/** @var InternalEntityFactory $entityFactory */
@@ -137,18 +134,16 @@ protected function getConcreteSheetIterator()
/**
* Closes the reader. To be used after reading the file.
- *
- * @return void
*/
protected function closeReader()
{
- if ($this->filePointer) {
+ 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);
+ ini_set('auto_detect_line_endings', $this->originalAutoDetectLineEndings);
}
}
}
diff --git a/lib/spout/src/Spout/Reader/CSV/RowIterator.php b/lib/openspout/src/Reader/CSV/RowIterator.php
similarity index 72%
rename from lib/spout/src/Spout/Reader/CSV/RowIterator.php
rename to lib/openspout/src/Reader/CSV/RowIterator.php
index c3f5592858b33..497d26637ae47 100644
--- a/lib/spout/src/Spout/Reader/CSV/RowIterator.php
+++ b/lib/openspout/src/Reader/CSV/RowIterator.php
@@ -1,33 +1,32 @@
rewindAndSkipBom();
@@ -96,40 +90,25 @@ public function rewind()
}
/**
- * This rewinds and skips the BOM if inserted at the beginning of the file
- * by moving the file pointer after it, so that it is not read.
+ * Checks if current position is valid.
*
- * @return void
- */
- protected function rewindAndSkipBom()
- {
- $byteOffsetToSkipBom = $this->encodingHelper->getBytesOffsetToSkipBOM($this->filePointer, $this->encoding);
-
- // sets the cursor after the BOM (0 means no BOM, so rewind it)
- $this->globalFunctionsHelper->fseek($this->filePointer, $byteOffsetToSkipBom);
- }
-
- /**
- * Checks if current position is valid
* @see http://php.net/manual/en/iterator.valid.php
- *
- * @return bool
*/
#[\ReturnTypeWillChange]
- public function valid()
+ public function valid(): bool
{
- return ($this->filePointer && !$this->hasReachedEndOfFile);
+ return $this->filePointer && !$this->hasReachedEndOfFile;
}
/**
* Move forward to next element. Reads data for the next unprocessed row.
+ *
* @see http://php.net/manual/en/iterator.next.php
*
- * @throws \Box\Spout\Common\Exception\EncodingConversionException If unable to convert data to UTF-8
- * @return void
+ * @throws \OpenSpout\Common\Exception\EncodingConversionException If unable to convert data to UTF-8
*/
#[\ReturnTypeWillChange]
- public function next()
+ public function next(): void
{
$this->hasReachedEndOfFile = $this->globalFunctionsHelper->feof($this->filePointer);
@@ -139,8 +118,50 @@ public function next()
}
/**
- * @throws \Box\Spout\Common\Exception\EncodingConversionException If unable to convert data to UTF-8
- * @return void
+ * Return the current element from the buffer.
+ *
+ * @see http://php.net/manual/en/iterator.current.php
+ */
+ #[\ReturnTypeWillChange]
+ public function current(): ?Row
+ {
+ return $this->rowBuffer;
+ }
+
+ /**
+ * Return the key of the current element.
+ *
+ * @see http://php.net/manual/en/iterator.key.php
+ */
+ #[\ReturnTypeWillChange]
+ public function key(): int
+ {
+ return $this->numReadRows;
+ }
+
+ /**
+ * Cleans up what was created to iterate over the object.
+ */
+ #[\ReturnTypeWillChange]
+ public function end(): void
+ {
+ // do nothing
+ }
+
+ /**
+ * This rewinds and skips the BOM if inserted at the beginning of the file
+ * by moving the file pointer after it, so that it is not read.
+ */
+ protected function rewindAndSkipBom()
+ {
+ $byteOffsetToSkipBom = $this->encodingHelper->getBytesOffsetToSkipBOM($this->filePointer, $this->encoding);
+
+ // sets the cursor after the BOM (0 means no BOM, so rewind it)
+ $this->globalFunctionsHelper->fseek($this->filePointer, $byteOffsetToSkipBom);
+ }
+
+ /**
+ * @throws \OpenSpout\Common\Exception\EncodingConversionException If unable to convert data to UTF-8
*/
protected function readDataForNextRow()
{
@@ -148,11 +169,11 @@ protected function readDataForNextRow()
$rowData = $this->getNextUTF8EncodedRow();
} while ($this->shouldReadNextRow($rowData));
- if ($rowData !== false) {
- // str_replace will replace NULL values by empty strings
- $rowDataBufferAsArray = \str_replace(null, null, $rowData);
+ if (false !== $rowData) {
+ // array_map will replace NULL values by empty strings
+ $rowDataBufferAsArray = array_map(function ($value) { return (string) $value; }, $rowData);
$this->rowBuffer = $this->entityFactory->createRowFromArray($rowDataBufferAsArray);
- $this->numReadRows++;
+ ++$this->numReadRows;
} else {
// If we reach this point, it means end of file was reached.
// This happens when the last lines are empty lines.
@@ -162,32 +183,34 @@ protected function readDataForNextRow()
/**
* @param array|bool $currentRowData
+ *
* @return bool Whether the data for the current row can be returned or if we need to keep reading
*/
protected function shouldReadNextRow($currentRowData)
{
- $hasSuccessfullyFetchedRowData = ($currentRowData !== false);
+ $hasSuccessfullyFetchedRowData = (false !== $currentRowData);
$hasNowReachedEndOfFile = $this->globalFunctionsHelper->feof($this->filePointer);
$isEmptyLine = $this->isEmptyLine($currentRowData);
- return (
- (!$hasSuccessfullyFetchedRowData && !$hasNowReachedEndOfFile) ||
- (!$this->shouldPreserveEmptyRows && $isEmptyLine)
- );
+ return
+ (!$hasSuccessfullyFetchedRowData && !$hasNowReachedEndOfFile)
+ || (!$this->shouldPreserveEmptyRows && $isEmptyLine)
+ ;
}
/**
* Returns the next row, converted if necessary to UTF-8.
* As fgetcsv() does not manage correctly encoding for non UTF-8 data,
- * we remove manually whitespace with ltrim or rtrim (depending on the order of the bytes)
+ * we remove manually whitespace with ltrim or rtrim (depending on the order of the bytes).
+ *
+ * @throws \OpenSpout\Common\Exception\EncodingConversionException If unable to convert data to UTF-8
*
- * @throws \Box\Spout\Common\Exception\EncodingConversionException If unable to convert data to UTF-8
* @return array|false The row for the current file pointer, encoded in UTF-8 or FALSE if nothing to read
*/
protected function getNextUTF8EncodedRow()
{
$encodedRowData = $this->globalFunctionsHelper->fgetcsv($this->filePointer, self::MAX_READ_BYTES_PER_LINE, $this->fieldDelimiter, $this->fieldEnclosure);
- if ($encodedRowData === false) {
+ if (false === $encodedRowData) {
return false;
}
@@ -196,13 +219,15 @@ protected function getNextUTF8EncodedRow()
case EncodingHelper::ENCODING_UTF16_LE:
case EncodingHelper::ENCODING_UTF32_LE:
// remove whitespace from the beginning of a string as fgetcsv() add extra whitespace when it try to explode non UTF-8 data
- $cellValue = \ltrim($cellValue);
+ $cellValue = ltrim($cellValue);
+
break;
case EncodingHelper::ENCODING_UTF16_BE:
case EncodingHelper::ENCODING_UTF32_BE:
// remove whitespace from the end of a string as fgetcsv() add extra whitespace when it try to explode non UTF-8 data
- $cellValue = \rtrim($cellValue);
+ $cellValue = rtrim($cellValue);
+
break;
}
@@ -214,44 +239,11 @@ protected function getNextUTF8EncodedRow()
/**
* @param array|bool $lineData Array containing the cells value for the line
+ *
* @return bool Whether the given line is empty
*/
protected function isEmptyLine($lineData)
{
- return (\is_array($lineData) && \count($lineData) === 1 && $lineData[0] === null);
- }
-
- /**
- * Return the current element from the buffer
- * @see http://php.net/manual/en/iterator.current.php
- *
- * @return Row|null
- */
- #[\ReturnTypeWillChange]
- public function current()
- {
- return $this->rowBuffer;
- }
-
- /**
- * Return the key of the current element
- * @see http://php.net/manual/en/iterator.key.php
- *
- * @return int
- */
- #[\ReturnTypeWillChange]
- public function key()
- {
- return $this->numReadRows;
- }
-
- /**
- * Cleans up what was created to iterate over the object.
- *
- * @return void
- */
- public function end()
- {
- // do nothing
+ return \is_array($lineData) && 1 === \count($lineData) && null === $lineData[0];
}
}
diff --git a/lib/spout/src/Spout/Reader/CSV/Sheet.php b/lib/openspout/src/Reader/CSV/Sheet.php
similarity index 82%
rename from lib/spout/src/Spout/Reader/CSV/Sheet.php
rename to lib/openspout/src/Reader/CSV/Sheet.php
index a3055a8d9b90c..f64b2ee6d8675 100644
--- a/lib/spout/src/Spout/Reader/CSV/Sheet.php
+++ b/lib/openspout/src/Reader/CSV/Sheet.php
@@ -1,15 +1,12 @@
hasReadUniqueSheet = false;
}
/**
- * Checks if current position is valid
- * @see http://php.net/manual/en/iterator.valid.php
+ * Checks if current position is valid.
*
- * @return bool
+ * @see http://php.net/manual/en/iterator.valid.php
*/
#[\ReturnTypeWillChange]
- public function valid()
+ public function valid(): bool
{
- return (!$this->hasReadUniqueSheet);
+ return !$this->hasReadUniqueSheet;
}
/**
- * Move forward to next element
- * @see http://php.net/manual/en/iterator.next.php
+ * Move forward to next element.
*
- * @return void
+ * @see http://php.net/manual/en/iterator.next.php
*/
#[\ReturnTypeWillChange]
- public function next()
+ public function next(): void
{
$this->hasReadUniqueSheet = true;
}
/**
- * Return the current element
- * @see http://php.net/manual/en/iterator.current.php
+ * Return the current element.
*
- * @return \Box\Spout\Reader\CSV\Sheet
+ * @see http://php.net/manual/en/iterator.current.php
*/
#[\ReturnTypeWillChange]
- public function current()
+ public function current(): Sheet
{
return $this->sheet;
}
/**
- * Return the key of the current element
- * @see http://php.net/manual/en/iterator.key.php
+ * Return the key of the current element.
*
- * @return int
+ * @see http://php.net/manual/en/iterator.key.php
*/
#[\ReturnTypeWillChange]
- public function key()
+ public function key(): int
{
return 1;
}
/**
* Cleans up what was created to iterate over the object.
- *
- * @return void
*/
- public function end()
+ #[\ReturnTypeWillChange]
+ public function end(): void
{
// do nothing
}
diff --git a/lib/spout/src/Spout/Reader/Common/Creator/InternalEntityFactoryInterface.php b/lib/openspout/src/Reader/Common/Creator/InternalEntityFactoryInterface.php
similarity index 64%
rename from lib/spout/src/Spout/Reader/Common/Creator/InternalEntityFactoryInterface.php
rename to lib/openspout/src/Reader/Common/Creator/InternalEntityFactoryInterface.php
index c1c78f1167416..e9cee4fe6a9c2 100644
--- a/lib/spout/src/Spout/Reader/Common/Creator/InternalEntityFactoryInterface.php
+++ b/lib/openspout/src/Reader/Common/Creator/InternalEntityFactoryInterface.php
@@ -1,23 +1,25 @@
getNumCells();
- if ($numCells === 0) {
+ if (0 === $numCells) {
return $row;
}
$rowCells = $row->getCells();
$maxCellIndex = $numCells;
- // If the row has empty cells, calling "setCellAtIndex" will add the cell
- // but in the wrong place (the new cell is added at the end of the array).
- // Therefore, we need to sort the array using keys to have proper order.
- // @see https://github.com/box/spout/issues/740
+ /**
+ * If the row has empty cells, calling "setCellAtIndex" will add the cell
+ * but in the wrong place (the new cell is added at the end of the array).
+ * Therefore, we need to sort the array using keys to have proper order.
+ *
+ * @see https://github.com/box/spout/issues/740
+ */
$needsSorting = false;
- for ($cellIndex = 0; $cellIndex < $maxCellIndex; $cellIndex++) {
+ for ($cellIndex = 0; $cellIndex < $maxCellIndex; ++$cellIndex) {
if (!isset($rowCells[$cellIndex])) {
$row->setCellAtIndex($this->entityFactory->createCell(''), $cellIndex);
$needsSorting = true;
diff --git a/lib/spout/src/Spout/Reader/Common/XMLProcessor.php b/lib/openspout/src/Reader/Common/XMLProcessor.php
similarity index 73%
rename from lib/spout/src/Spout/Reader/Common/XMLProcessor.php
rename to lib/openspout/src/Reader/Common/XMLProcessor.php
index 05ee970b57cdc..967c0ffc6da76 100644
--- a/lib/spout/src/Spout/Reader/Common/XMLProcessor.php
+++ b/lib/openspout/src/Reader/Common/XMLProcessor.php
@@ -1,35 +1,34 @@
xmlReader->read()) {
+ $nodeType = $this->xmlReader->nodeType;
+ $nodeNamePossiblyWithPrefix = $this->xmlReader->name;
+ $nodeNameWithoutPrefix = $this->xmlReader->localName;
+
+ $callbackData = $this->getRegisteredCallbackData($nodeNamePossiblyWithPrefix, $nodeNameWithoutPrefix, $nodeType);
+
+ if (null !== $callbackData) {
+ $callbackResponse = $this->invokeCallback($callbackData, [$this->xmlReader]);
+
+ if (self::PROCESSING_STOP === $callbackResponse) {
+ // stop reading
+ break;
+ }
+ }
+ }
+ }
+
/**
* @param string $nodeName Name of the node
- * @param int $nodeType Type of the node [NODE_TYPE_START || NODE_TYPE_END]
+ * @param int $nodeType Type of the node [NODE_TYPE_START || NODE_TYPE_END]
+ *
* @return string Key used to store the associated callback
*/
private function getCallbackKey($nodeName, $nodeType)
{
- return "$nodeName$nodeType";
+ return "{$nodeName}{$nodeType}";
}
/**
@@ -67,6 +94,7 @@ private function getCallbackKey($nodeName, $nodeType)
* will be needed to invoke the callback later.
*
* @param callable $callback Array reference to a callback: [OBJECT, METHOD_NAME]
+ *
* @return array Associative array containing the elements needed to invoke the callback using Reflection
*/
private function getInvokableCallbackData($callback)
@@ -82,38 +110,12 @@ private function getInvokableCallbackData($callback)
];
}
- /**
- * Resumes the reading of the XML file where it was left off.
- * Stops whenever a callback indicates that reading should stop or at the end of the file.
- *
- * @throws \Box\Spout\Reader\Exception\XMLProcessingException
- * @return void
- */
- public function readUntilStopped()
- {
- while ($this->xmlReader->read()) {
- $nodeType = $this->xmlReader->nodeType;
- $nodeNamePossiblyWithPrefix = $this->xmlReader->name;
- $nodeNameWithoutPrefix = $this->xmlReader->localName;
-
- $callbackData = $this->getRegisteredCallbackData($nodeNamePossiblyWithPrefix, $nodeNameWithoutPrefix, $nodeType);
-
- if ($callbackData !== null) {
- $callbackResponse = $this->invokeCallback($callbackData, [$this->xmlReader]);
-
- if ($callbackResponse === self::PROCESSING_STOP) {
- // stop reading
- break;
- }
- }
- }
- }
-
/**
* @param string $nodeNamePossiblyWithPrefix Name of the node, possibly prefixed
- * @param string $nodeNameWithoutPrefix Name of the same node, un-prefixed
- * @param int $nodeType Type of the node [NODE_TYPE_START || NODE_TYPE_END]
- * @return array|null Callback data to be used for execution when a node of the given name/type is read or NULL if none found
+ * @param string $nodeNameWithoutPrefix Name of the same node, un-prefixed
+ * @param int $nodeType Type of the node [NODE_TYPE_START || NODE_TYPE_END]
+ *
+ * @return null|array Callback data to be used for execution when a node of the given name/type is read or NULL if none found
*/
private function getRegisteredCallbackData($nodeNamePossiblyWithPrefix, $nodeNameWithoutPrefix, $nodeType)
{
@@ -130,12 +132,13 @@ private function getRegisteredCallbackData($nodeNamePossiblyWithPrefix, $nodeNam
}
// Using isset here because it is way faster than array_key_exists...
- return isset($this->callbacks[$callbackKeyToUse]) ? $this->callbacks[$callbackKeyToUse] : null;
+ return $this->callbacks[$callbackKeyToUse] ?? null;
}
/**
* @param array $callbackData Associative array containing data to invoke the callback using Reflection
- * @param array $args Arguments to pass to the callback
+ * @param array $args Arguments to pass to the callback
+ *
* @return int Callback response
*/
private function invokeCallback($callbackData, $args)
diff --git a/lib/spout/src/Spout/Reader/Exception/InvalidValueException.php b/lib/openspout/src/Reader/Exception/InvalidValueException.php
similarity index 74%
rename from lib/spout/src/Spout/Reader/Exception/InvalidValueException.php
rename to lib/openspout/src/Reader/Exception/InvalidValueException.php
index 6ed0b6d0e0e97..9bbcebdf03b0c 100644
--- a/lib/spout/src/Spout/Reader/Exception/InvalidValueException.php
+++ b/lib/openspout/src/Reader/Exception/InvalidValueException.php
@@ -1,22 +1,18 @@
helperFactory = $helperFactory;
@@ -35,8 +30,9 @@ public function __construct(HelperFactory $helperFactory, ManagerFactory $manage
}
/**
- * @param string $filePath Path of the file to be read
- * @param \Box\Spout\Common\Manager\OptionsManagerInterface $optionsManager Reader's options manager
+ * @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)
@@ -48,12 +44,13 @@ public function createSheetIterator($filePath, $optionsManager)
}
/**
- * @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 \Box\Spout\Common\Manager\OptionsManagerInterface $optionsManager Reader's options manager
+ * @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)
@@ -63,23 +60,9 @@ public function createSheet($xmlReader, $sheetIndex, $sheetName, $isSheetActive,
return new Sheet($rowIterator, $sheetIndex, $sheetName, $isSheetActive, $isSheetVisible);
}
- /**
- * @param XMLReader $xmlReader XML Reader
- * @param \Box\Spout\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 Cell[] $cells
+ *
* @return Row
*/
public function createRow(array $cells = [])
@@ -89,6 +72,7 @@ public function createRow(array $cells = [])
/**
* @param mixed $cellValue
+ *
* @return Cell
*/
public function createCell($cellValue)
@@ -105,19 +89,36 @@ public function createXMLReader()
}
/**
- * @param $xmlReader
- * @return XMLProcessor
+ * @return \ZipArchive
*/
- private function createXMLProcessor($xmlReader)
+ public function createZipArchive()
{
- return new XMLProcessor($xmlReader);
+ return new \ZipArchive();
}
/**
- * @return \ZipArchive
+ * @param XMLReader $xmlReader XML Reader
+ * @param \OpenSpout\Common\Manager\OptionsManagerInterface $optionsManager Reader's options manager
+ *
+ * @return RowIterator
*/
- public function createZipArchive()
+ private function createRowIterator($xmlReader, $optionsManager)
{
- return new \ZipArchive();
+ $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);
}
}
diff --git a/lib/spout/src/Spout/Reader/ODS/Creator/ManagerFactory.php b/lib/openspout/src/Reader/ODS/Creator/ManagerFactory.php
similarity index 65%
rename from lib/spout/src/Spout/Reader/ODS/Creator/ManagerFactory.php
rename to lib/openspout/src/Reader/ODS/Creator/ManagerFactory.php
index 3644d15e991c4..546069e0fe880 100644
--- a/lib/spout/src/Spout/Reader/ODS/Creator/ManagerFactory.php
+++ b/lib/openspout/src/Reader/ODS/Creator/ManagerFactory.php
@@ -1,17 +1,17 @@
formatStringCellValue($node);
+
case self::CELL_TYPE_FLOAT:
return $this->formatFloatCellValue($node);
+
case self::CELL_TYPE_BOOLEAN:
return $this->formatBooleanCellValue($node);
+
case self::CELL_TYPE_DATE:
return $this->formatDateCellValue($node);
+
case self::CELL_TYPE_TIME:
return $this->formatTimeCellValue($node);
+
case self::CELL_TYPE_CURRENCY:
return $this->formatCurrencyCellValue($node);
+
case self::CELL_TYPE_PERCENTAGE:
return $this->formatPercentageCellValue($node);
+
case self::CELL_TYPE_VOID:
default:
return '';
@@ -96,7 +105,8 @@ public function extractAndFormatNodeValue($node)
/**
* Returns the cell String value.
*
- * @param \DOMNode $node
+ * @param \DOMElement $node
+ *
* @return string The value associated with the cell
*/
protected function formatStringCellValue($node)
@@ -108,73 +118,17 @@ protected function formatStringCellValue($node)
$pNodeValues[] = $this->extractTextValueFromNode($pNode);
}
- $escapedCellValue = \implode("\n", $pNodeValues);
- $cellValue = $this->escaper->unescape($escapedCellValue);
-
- return $cellValue;
- }
-
- /**
- * @param $pNode
- * @return string
- */
- private function extractTextValueFromNode($pNode)
- {
- $textValue = '';
-
- foreach ($pNode->childNodes as $childNode) {
- if ($childNode instanceof \DOMText) {
- $textValue .= $childNode->nodeValue;
- } elseif ($this->isWhitespaceNode($childNode->nodeName)) {
- $textValue .= $this->transformWhitespaceNode($childNode);
- } elseif ($childNode->nodeName === self::XML_NODE_TEXT_A || $childNode->nodeName === self::XML_NODE_TEXT_SPAN) {
- $textValue .= $this->extractTextValueFromNode($childNode);
- }
- }
+ $escapedCellValue = implode("\n", $pNodeValues);
- return $textValue;
- }
-
- /**
- * Returns whether the given node is a whitespace node. It must be one of these:
- * -
- * -
- * -
- *
- * @param string $nodeName
- * @return bool
- */
- private function isWhitespaceNode($nodeName)
- {
- return isset(self::$WHITESPACE_XML_NODES[$nodeName]);
- }
-
- /**
- * The "" node can contain the string value directly
- * or contain child elements. In this case, whitespaces contain in
- * the child elements should be replaced by their XML equivalent:
- * - space =>
- * - tab =>
- * - line break =>
- *
- * @see https://docs.oasis-open.org/office/v1.2/os/OpenDocument-v1.2-os-part1.html#__RefHeading__1415200_253892949
- *
- * @param \DOMNode $node The XML node representing a whitespace
- * @return string The corresponding whitespace value
- */
- private function transformWhitespaceNode($node)
- {
- $countAttribute = $node->getAttribute(self::XML_ATTRIBUTE_C); // only defined for ""
- $numWhitespaces = (!empty($countAttribute)) ? (int) $countAttribute : 1;
-
- return \str_repeat(self::$WHITESPACE_XML_NODES[$node->nodeName], $numWhitespaces);
+ return $this->escaper->unescape($escapedCellValue);
}
/**
* Returns the cell Numeric value from the given node.
*
- * @param \DOMNode $node
- * @return int|float The value associated with the cell
+ * @param \DOMElement $node
+ *
+ * @return float|int The value associated with the cell
*/
protected function formatFloatCellValue($node)
{
@@ -182,15 +136,15 @@ protected function formatFloatCellValue($node)
$nodeIntValue = (int) $nodeValue;
$nodeFloatValue = (float) $nodeValue;
- $cellValue = ((float) $nodeIntValue === $nodeFloatValue) ? $nodeIntValue : $nodeFloatValue;
- return $cellValue;
+ return ((float) $nodeIntValue === $nodeFloatValue) ? $nodeIntValue : $nodeFloatValue;
}
/**
* Returns the cell Boolean value from the given node.
*
- * @param \DOMNode $node
+ * @param \DOMElement $node
+ *
* @return bool The value associated with the cell
*/
protected function formatBooleanCellValue($node)
@@ -203,8 +157,10 @@ protected function formatBooleanCellValue($node)
/**
* Returns the cell Date value from the given node.
*
- * @param \DOMNode $node
+ * @param \DOMElement $node
+ *
* @throws InvalidValueException If the value is not a valid date
+ *
* @return \DateTime|string The value associated with the cell
*/
protected function formatDateCellValue($node)
@@ -221,6 +177,7 @@ protected function formatDateCellValue($node)
} else {
// otherwise, get it from the "date-value" attribute
$nodeValue = $node->getAttribute(self::XML_ATTRIBUTE_DATE_VALUE);
+
try {
$cellValue = new \DateTime($nodeValue);
} catch (\Exception $e) {
@@ -234,8 +191,10 @@ protected function formatDateCellValue($node)
/**
* Returns the cell Time value from the given node.
*
- * @param \DOMNode $node
+ * @param \DOMElement $node
+ *
* @throws InvalidValueException If the value is not a valid time
+ *
* @return \DateInterval|string The value associated with the cell
*/
protected function formatTimeCellValue($node)
@@ -252,6 +211,7 @@ protected function formatTimeCellValue($node)
} else {
// otherwise, get it from the "time-value" attribute
$nodeValue = $node->getAttribute(self::XML_ATTRIBUTE_TIME_VALUE);
+
try {
$cellValue = new \DateInterval($nodeValue);
} catch (\Exception $e) {
@@ -265,7 +225,8 @@ protected function formatTimeCellValue($node)
/**
* Returns the cell Currency value from the given node.
*
- * @param \DOMNode $node
+ * @param \DOMElement $node
+ *
* @return string The value associated with the cell (e.g. "100 USD" or "9.99 EUR")
*/
protected function formatCurrencyCellValue($node)
@@ -273,18 +234,78 @@ protected function formatCurrencyCellValue($node)
$value = $node->getAttribute(self::XML_ATTRIBUTE_VALUE);
$currency = $node->getAttribute(self::XML_ATTRIBUTE_CURRENCY);
- return "$value $currency";
+ return "{$value} {$currency}";
}
/**
* Returns the cell Percentage value from the given node.
*
- * @param \DOMNode $node
- * @return int|float The value associated with the cell
+ * @param \DOMElement $node
+ *
+ * @return float|int The value associated with the cell
*/
protected function formatPercentageCellValue($node)
{
// percentages are formatted like floats
return $this->formatFloatCellValue($node);
}
+
+ /**
+ * @param \DOMNode $pNode
+ *
+ * @return string
+ */
+ private function extractTextValueFromNode($pNode)
+ {
+ $textValue = '';
+
+ foreach ($pNode->childNodes as $childNode) {
+ if ($childNode instanceof \DOMText) {
+ $textValue .= $childNode->nodeValue;
+ } elseif ($this->isWhitespaceNode($childNode->nodeName)) {
+ $textValue .= $this->transformWhitespaceNode($childNode);
+ } elseif (self::XML_NODE_TEXT_A === $childNode->nodeName || self::XML_NODE_TEXT_SPAN === $childNode->nodeName) {
+ $textValue .= $this->extractTextValueFromNode($childNode);
+ }
+ }
+
+ return $textValue;
+ }
+
+ /**
+ * Returns whether the given node is a whitespace node. It must be one of these:
+ * -
+ * -
+ * - .
+ *
+ * @param string $nodeName
+ *
+ * @return bool
+ */
+ private function isWhitespaceNode($nodeName)
+ {
+ return isset(self::$WHITESPACE_XML_NODES[$nodeName]);
+ }
+
+ /**
+ * The "" node can contain the string value directly
+ * or contain child elements. In this case, whitespaces contain in
+ * the child elements should be replaced by their XML equivalent:
+ * - space =>
+ * - tab =>
+ * - line break => .
+ *
+ * @see https://docs.oasis-open.org/office/v1.2/os/OpenDocument-v1.2-os-part1.html#__RefHeading__1415200_253892949
+ *
+ * @param \DOMElement $node The XML node representing a whitespace
+ *
+ * @return string The corresponding whitespace value
+ */
+ private function transformWhitespaceNode($node)
+ {
+ $countAttribute = $node->getAttribute(self::XML_ATTRIBUTE_C); // only defined for ""
+ $numWhitespaces = (!empty($countAttribute)) ? (int) $countAttribute : 1;
+
+ return str_repeat(self::$WHITESPACE_XML_NODES[$node->nodeName], $numWhitespaces);
+ }
}
diff --git a/lib/spout/src/Spout/Reader/ODS/Helper/SettingsHelper.php b/lib/openspout/src/Reader/ODS/Helper/SettingsHelper.php
similarity index 63%
rename from lib/spout/src/Spout/Reader/ODS/Helper/SettingsHelper.php
rename to lib/openspout/src/Reader/ODS/Helper/SettingsHelper.php
index 7d4782187aac6..4463eeb3e91bd 100644
--- a/lib/spout/src/Spout/Reader/ODS/Helper/SettingsHelper.php
+++ b/lib/openspout/src/Reader/ODS/Helper/SettingsHelper.php
@@ -1,22 +1,21 @@
entityFactory->createXMLReader();
- if ($xmlReader->openFileInZip($filePath, self::SETTINGS_XML_FILE_PATH) === false) {
+ if (false === $xmlReader->openFileInZip($filePath, self::SETTINGS_XML_FILE_PATH)) {
return null;
}
@@ -44,8 +44,9 @@ public function getActiveSheetName($filePath)
try {
while ($xmlReader->readUntilNodeFound(self::XML_NODE_CONFIG_ITEM)) {
- if ($xmlReader->getAttribute(self::XML_ATTRIBUTE_CONFIG_NAME) === self::XML_ATTRIBUTE_VALUE_ACTIVE_TABLE) {
+ if (self::XML_ATTRIBUTE_VALUE_ACTIVE_TABLE === $xmlReader->getAttribute(self::XML_ATTRIBUTE_CONFIG_NAME)) {
$activeSheetName = $xmlReader->readString();
+
break;
}
}
diff --git a/lib/spout/src/Spout/Reader/ODS/Manager/OptionsManager.php b/lib/openspout/src/Reader/ODS/Manager/OptionsManager.php
similarity index 72%
rename from lib/spout/src/Spout/Reader/ODS/Manager/OptionsManager.php
rename to lib/openspout/src/Reader/ODS/Manager/OptionsManager.php
index 102eccb6689b9..e13c5446dc598 100644
--- a/lib/spout/src/Spout/Reader/ODS/Manager/OptionsManager.php
+++ b/lib/openspout/src/Reader/ODS/Manager/OptionsManager.php
@@ -1,13 +1,12 @@
zip = $entityFactory->createZipArchive();
- if ($this->zip->open($filePath) === true) {
+ 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.");
+ throw new IOException("Could not open {$filePath} for reading.");
}
}
@@ -64,12 +63,10 @@ protected function getConcreteSheetIterator()
/**
* Closes the reader. To be used after reading the file.
- *
- * @return void
*/
protected function closeReader()
{
- if ($this->zip) {
+ if (null !== $this->zip) {
$this->zip->close();
}
}
diff --git a/lib/spout/src/Spout/Reader/ODS/RowIterator.php b/lib/openspout/src/Reader/ODS/RowIterator.php
similarity index 74%
rename from lib/spout/src/Spout/Reader/ODS/RowIterator.php
rename to lib/openspout/src/Reader/ODS/RowIterator.php
index 27a06001a65c2..a3ab7aa4fd0a8 100644
--- a/lib/spout/src/Spout/Reader/ODS/RowIterator.php
+++ b/lib/openspout/src/Reader/ODS/RowIterator.php
@@ -1,41 +1,38 @@
" element
- * @param OptionsManagerInterface $optionsManager Reader's options manager
- * @param CellValueFormatter $cellValueFormatter Helper to format cell values
- * @param XMLProcessor $xmlProcessor Helper to process XML files
- * @param RowManager $rowManager Manages rows
- * @param InternalEntityFactory $entityFactory Factory to create entities
+ * @param XMLReader $xmlReader XML Reader, positioned on the "" element
+ * @param OptionsManagerInterface $optionsManager Reader's options manager
+ * @param CellValueFormatter $cellValueFormatter Helper to format cell values
+ * @param XMLProcessor $xmlProcessor Helper to process XML files
+ * @param RowManager $rowManager Manages rows
+ * @param InternalEntityFactory $entityFactory Factory to create entities
*/
public function __construct(
XMLReader $xmlReader,
@@ -113,13 +110,13 @@ public function __construct(
/**
* Rewind the Iterator to the first element.
* NOTE: It can only be done once, as it is not possible to read an XML file backwards.
+ *
* @see http://php.net/manual/en/iterator.rewind.php
*
- * @throws \Box\Spout\Reader\Exception\IteratorNotRewindableException If the iterator is rewound more than once
- * @return void
+ * @throws \OpenSpout\Reader\Exception\IteratorNotRewindableException If the iterator is rewound more than once
*/
#[\ReturnTypeWillChange]
- public function rewind()
+ public function rewind(): void
{
// Because sheet and row data is located in the file, we can't rewind both the
// sheet iterator and the row iterator, as XML file cannot be read backwards.
@@ -138,33 +135,63 @@ public function rewind()
}
/**
- * Checks if current position is valid
- * @see http://php.net/manual/en/iterator.valid.php
+ * Checks if current position is valid.
*
- * @return bool
+ * @see http://php.net/manual/en/iterator.valid.php
*/
#[\ReturnTypeWillChange]
- public function valid()
+ public function valid(): bool
{
- return (!$this->hasReachedEndOfFile);
+ return !$this->hasReachedEndOfFile;
}
/**
* Move forward to next element. Empty rows will be skipped.
+ *
* @see http://php.net/manual/en/iterator.next.php
*
- * @throws \Box\Spout\Reader\Exception\SharedStringNotFoundException If a shared string was not found
- * @throws \Box\Spout\Common\Exception\IOException If unable to read the sheet data XML
- * @return void
+ * @throws \OpenSpout\Reader\Exception\SharedStringNotFoundException If a shared string was not found
+ * @throws \OpenSpout\Common\Exception\IOException If unable to read the sheet data XML
*/
#[\ReturnTypeWillChange]
- public function next()
+ public function next(): void
{
if ($this->doesNeedDataForNextRowToBeProcessed()) {
$this->readDataForNextRow();
}
- $this->lastRowIndexProcessed++;
+ ++$this->lastRowIndexProcessed;
+ }
+
+ /**
+ * Return the current element, from the buffer.
+ *
+ * @see http://php.net/manual/en/iterator.current.php
+ */
+ #[\ReturnTypeWillChange]
+ public function current(): Row
+ {
+ return $this->rowBuffer;
+ }
+
+ /**
+ * Return the key of the current element.
+ *
+ * @see http://php.net/manual/en/iterator.key.php
+ */
+ #[\ReturnTypeWillChange]
+ public function key(): int
+ {
+ return $this->lastRowIndexProcessed;
+ }
+
+ /**
+ * Cleans up what was created to iterate over the object.
+ */
+ #[\ReturnTypeWillChange]
+ public function end(): void
+ {
+ $this->xmlReader->close();
}
/**
@@ -172,24 +199,23 @@ public function next()
* We DO need to read data if:
* - we have not read any rows yet
* OR
- * - the next row to be processed immediately follows the last read row
+ * - the next row to be processed immediately follows the last read row.
*
- * @return bool Whether we need data for the next row to be processed.
+ * @return bool whether we need data for the next row to be processed
*/
protected function doesNeedDataForNextRowToBeProcessed()
{
- $hasReadAtLeastOneRow = ($this->lastRowIndexProcessed !== 0);
+ $hasReadAtLeastOneRow = (0 !== $this->lastRowIndexProcessed);
- return (
- !$hasReadAtLeastOneRow ||
- $this->lastRowIndexProcessed === $this->nextRowIndexToBeProcessed - 1
- );
+ return
+ !$hasReadAtLeastOneRow
+ || $this->lastRowIndexProcessed === $this->nextRowIndexToBeProcessed - 1
+ ;
}
/**
- * @throws \Box\Spout\Reader\Exception\SharedStringNotFoundException If a shared string was not found
- * @throws \Box\Spout\Common\Exception\IOException If unable to read the sheet data XML
- * @return void
+ * @throws \OpenSpout\Reader\Exception\SharedStringNotFoundException If a shared string was not found
+ * @throws \OpenSpout\Common\Exception\IOException If unable to read the sheet data XML
*/
protected function readDataForNextRow()
{
@@ -205,7 +231,8 @@ protected function readDataForNextRow()
}
/**
- * @param \Box\Spout\Reader\Wrapper\XMLReader $xmlReader XMLReader object, positioned on a "" starting node
+ * @param \OpenSpout\Reader\Wrapper\XMLReader $xmlReader XMLReader object, positioned on a "" starting node
+ *
* @return int A return code that indicates what action should the processor take next
*/
protected function processRowStartingNode($xmlReader)
@@ -220,7 +247,8 @@ protected function processRowStartingNode($xmlReader)
}
/**
- * @param \Box\Spout\Reader\Wrapper\XMLReader $xmlReader XMLReader object, positioned on a "" starting node
+ * @param \OpenSpout\Reader\Wrapper\XMLReader $xmlReader XMLReader object, positioned on a "" starting node
+ *
* @return int A return code that indicates what action should the processor take next
*/
protected function processCellStartingNode($xmlReader)
@@ -228,12 +256,13 @@ protected function processCellStartingNode($xmlReader)
$currentNumColumnsRepeated = $this->getNumColumnsRepeatedForCurrentNode($xmlReader);
// NOTE: expand() will automatically decode all XML entities of the child nodes
+ /** @var \DOMElement $node */
$node = $xmlReader->expand();
$currentCell = $this->getCell($node);
// process cell N only after having read cell N+1 (see below why)
if ($this->hasAlreadyReadOneCellInCurrentRow) {
- for ($i = 0; $i < $this->numColumnsRepeated; $i++) {
+ for ($i = 0; $i < $this->numColumnsRepeated; ++$i) {
$this->currentlyProcessedRow->addCell($this->lastProcessedCell);
}
}
@@ -269,7 +298,7 @@ protected function processRowEndingNode()
// In Excel, the number of supported columns is 16384, but we don't want to returns rows with
// always 16384 cells.
if (($numCellsInCurrentlyProcessedRow + $actualNumColumnsRepeated) !== self::MAX_COLUMNS_EXCEL) {
- for ($i = 0; $i < $actualNumColumnsRepeated; $i++) {
+ for ($i = 0; $i < $actualNumColumnsRepeated; ++$i) {
$this->currentlyProcessedRow->addCell($this->lastProcessedCell);
}
}
@@ -295,31 +324,34 @@ protected function processTableEndingNode()
}
/**
- * @param \Box\Spout\Reader\Wrapper\XMLReader $xmlReader XMLReader object, positioned on a "" starting node
+ * @param \OpenSpout\Reader\Wrapper\XMLReader $xmlReader XMLReader object, positioned on a "" starting node
+ *
* @return int The value of "table:number-rows-repeated" attribute of the current node, or 1 if attribute missing
*/
protected function getNumRowsRepeatedForCurrentNode($xmlReader)
{
$numRowsRepeated = $xmlReader->getAttribute(self::XML_ATTRIBUTE_NUM_ROWS_REPEATED);
- return ($numRowsRepeated !== null) ? (int) $numRowsRepeated : 1;
+ return (null !== $numRowsRepeated) ? (int) $numRowsRepeated : 1;
}
/**
- * @param \Box\Spout\Reader\Wrapper\XMLReader $xmlReader XMLReader object, positioned on a "" starting node
+ * @param \OpenSpout\Reader\Wrapper\XMLReader $xmlReader XMLReader object, positioned on a "" starting node
+ *
* @return int The value of "table:number-columns-repeated" attribute of the current node, or 1 if attribute missing
*/
protected function getNumColumnsRepeatedForCurrentNode($xmlReader)
{
$numColumnsRepeated = $xmlReader->getAttribute(self::XML_ATTRIBUTE_NUM_COLUMNS_REPEATED);
- return ($numColumnsRepeated !== null) ? (int) $numColumnsRepeated : 1;
+ return (null !== $numColumnsRepeated) ? (int) $numColumnsRepeated : 1;
}
/**
* Returns the cell with (unescaped) correctly marshalled, cell value associated to the given XML node.
*
- * @param \DOMNode $node
+ * @param \DOMElement $node
+ *
* @return Cell The cell set with the associated with the cell
*/
protected function getCell($node)
@@ -341,50 +373,16 @@ protected function getCell($node)
* After finishing processing each cell, the last read cell is not part of the
* row data yet (as we still need to apply the "num-columns-repeated" attribute).
*
- * @param Row $currentRow
- * @param Cell $lastReadCell The last read cell
+ * @param Row $currentRow
+ * @param null|Cell $lastReadCell The last read cell
+ *
* @return bool Whether the row is empty
*/
protected function isEmptyRow($currentRow, $lastReadCell)
{
- return (
- $this->rowManager->isEmpty($currentRow) &&
- (!isset($lastReadCell) || $lastReadCell->isEmpty())
- );
- }
-
- /**
- * Return the current element, from the buffer.
- * @see http://php.net/manual/en/iterator.current.php
- *
- * @return Row
- */
- #[\ReturnTypeWillChange]
- public function current()
- {
- return $this->rowBuffer;
- }
-
- /**
- * Return the key of the current element
- * @see http://php.net/manual/en/iterator.key.php
- *
- * @return int
- */
- #[\ReturnTypeWillChange]
- public function key()
- {
- return $this->lastRowIndexProcessed;
- }
-
- /**
- * Cleans up what was created to iterate over the object.
- *
- * @return void
- */
- #[\ReturnTypeWillChange]
- public function end()
- {
- $this->xmlReader->close();
+ return
+ $this->rowManager->isEmpty($currentRow)
+ && (!isset($lastReadCell) || $lastReadCell->isEmpty())
+ ;
}
}
diff --git a/lib/spout/src/Spout/Reader/ODS/Sheet.php b/lib/openspout/src/Reader/ODS/Sheet.php
similarity index 71%
rename from lib/spout/src/Spout/Reader/ODS/Sheet.php
rename to lib/openspout/src/Reader/ODS/Sheet.php
index 74ec61f1ad7c5..306f26888346a 100644
--- a/lib/spout/src/Spout/Reader/ODS/Sheet.php
+++ b/lib/openspout/src/Reader/ODS/Sheet.php
@@ -1,16 +1,15 @@
xmlReader->close();
- if ($this->xmlReader->openFileInZip($this->filePath, self::CONTENT_XML_FILE_PATH) === false) {
- $contentXmlFilePath = $this->filePath . '#' . self::CONTENT_XML_FILE_PATH;
+ if (false === $this->xmlReader->openFileInZip($this->filePath, self::CONTENT_XML_FILE_PATH)) {
+ $contentXmlFilePath = $this->filePath.'#'.self::CONTENT_XML_FILE_PATH;
+
throw new IOException("Could not open \"{$contentXmlFilePath}\".");
}
@@ -100,34 +100,8 @@ public function rewind()
}
/**
- * Extracts the visibility of the sheets
+ * Checks if current position is valid.
*
- * @return array Associative array [STYLE_NAME] => [IS_SHEET_VISIBLE]
- */
- private function readSheetsVisibility()
- {
- $sheetsVisibility = [];
-
- $this->xmlReader->readUntilNodeFound(self::XML_NODE_AUTOMATIC_STYLES);
- $automaticStylesNode = $this->xmlReader->expand();
-
- $tableStyleNodes = $automaticStylesNode->getElementsByTagNameNS(self::XML_STYLE_NAMESPACE, self::XML_NODE_STYLE_TABLE_PROPERTIES);
-
- /** @var \DOMElement $tableStyleNode */
- foreach ($tableStyleNodes as $tableStyleNode) {
- $isSheetVisible = ($tableStyleNode->getAttribute(self::XML_ATTRIBUTE_TABLE_DISPLAY) !== 'false');
-
- $parentStyleNode = $tableStyleNode->parentNode;
- $styleName = $parentStyleNode->getAttribute(self::XML_ATTRIBUTE_STYLE_NAME);
-
- $sheetsVisibility[$styleName] = $isSheetVisible;
- }
-
- return $sheetsVisibility;
- }
-
- /**
- * Checks if current position is valid
* @see http://php.net/manual/en/iterator.valid.php
*
* @return bool
@@ -139,10 +113,9 @@ public function valid()
}
/**
- * Move forward to next element
- * @see http://php.net/manual/en/iterator.next.php
+ * Move forward to next element.
*
- * @return void
+ * @see http://php.net/manual/en/iterator.next.php
*/
#[\ReturnTypeWillChange]
public function next()
@@ -150,15 +123,16 @@ public function next()
$this->hasFoundSheet = $this->xmlReader->readUntilNodeFound(self::XML_NODE_TABLE);
if ($this->hasFoundSheet) {
- $this->currentSheetIndex++;
+ ++$this->currentSheetIndex;
}
}
/**
- * Return the current element
+ * Return the current element.
+ *
* @see http://php.net/manual/en/iterator.current.php
*
- * @return \Box\Spout\Reader\ODS\Sheet
+ * @return \OpenSpout\Reader\ODS\Sheet
*/
#[\ReturnTypeWillChange]
public function current()
@@ -182,56 +156,84 @@ public function current()
}
/**
- * Returns whether the current sheet was defined as the active one
+ * Return the key of the current element.
*
- * @param string $sheetName Name of the current sheet
- * @param int $sheetIndex Index of the current sheet
- * @param string|null $activeSheetName Name of the sheet that was defined as active or NULL if none defined
- * @return bool Whether the current sheet was defined as the active one
+ * @see http://php.net/manual/en/iterator.key.php
+ *
+ * @return int
*/
- private function isSheetActive($sheetName, $sheetIndex, $activeSheetName)
+ #[\ReturnTypeWillChange]
+ public function key()
{
- // The given sheet is active if its name matches the defined active sheet's name
- // or if no information about the active sheet was found, it defaults to the first sheet.
- return (
- ($activeSheetName === null && $sheetIndex === 0) ||
- ($activeSheetName === $sheetName)
- );
+ return $this->currentSheetIndex + 1;
}
/**
- * Returns whether the current sheet is visible
+ * Cleans up what was created to iterate over the object.
+ */
+ #[\ReturnTypeWillChange]
+ public function end()
+ {
+ $this->xmlReader->close();
+ }
+
+ /**
+ * Extracts the visibility of the sheets.
*
- * @param string $sheetStyleName Name of the sheet style
- * @return bool Whether the current sheet is visible
+ * @return array Associative array [STYLE_NAME] => [IS_SHEET_VISIBLE]
*/
- private function isSheetVisible($sheetStyleName)
+ private function readSheetsVisibility()
{
- return isset($this->sheetsVisibility[$sheetStyleName]) ?
- $this->sheetsVisibility[$sheetStyleName] :
- true;
+ $sheetsVisibility = [];
+
+ $this->xmlReader->readUntilNodeFound(self::XML_NODE_AUTOMATIC_STYLES);
+ /** @var \DOMElement $automaticStylesNode */
+ $automaticStylesNode = $this->xmlReader->expand();
+
+ $tableStyleNodes = $automaticStylesNode->getElementsByTagNameNS(self::XML_STYLE_NAMESPACE, self::XML_NODE_STYLE_TABLE_PROPERTIES);
+
+ /** @var \DOMElement $tableStyleNode */
+ foreach ($tableStyleNodes as $tableStyleNode) {
+ $isSheetVisible = ('false' !== $tableStyleNode->getAttribute(self::XML_ATTRIBUTE_TABLE_DISPLAY));
+
+ $parentStyleNode = $tableStyleNode->parentNode;
+ $styleName = $parentStyleNode->getAttribute(self::XML_ATTRIBUTE_STYLE_NAME);
+
+ $sheetsVisibility[$styleName] = $isSheetVisible;
+ }
+
+ return $sheetsVisibility;
}
/**
- * Return the key of the current element
- * @see http://php.net/manual/en/iterator.key.php
+ * Returns whether the current sheet was defined as the active one.
*
- * @return int
+ * @param string $sheetName Name of the current sheet
+ * @param int $sheetIndex Index of the current sheet
+ * @param null|string $activeSheetName Name of the sheet that was defined as active or NULL if none defined
+ *
+ * @return bool Whether the current sheet was defined as the active one
*/
- #[\ReturnTypeWillChange]
- public function key()
+ private function isSheetActive($sheetName, $sheetIndex, $activeSheetName)
{
- return $this->currentSheetIndex + 1;
+ // The given sheet is active if its name matches the defined active sheet's name
+ // or if no information about the active sheet was found, it defaults to the first sheet.
+ return
+ (null === $activeSheetName && 0 === $sheetIndex)
+ || ($activeSheetName === $sheetName)
+ ;
}
/**
- * Cleans up what was created to iterate over the object.
+ * Returns whether the current sheet is visible.
*
- * @return void
+ * @param string $sheetStyleName Name of the sheet style
+ *
+ * @return bool Whether the current sheet is visible
*/
- #[\ReturnTypeWillChange]
- public function end()
+ private function isSheetVisible($sheetStyleName)
{
- $this->xmlReader->close();
+ return $this->sheetsVisibility[$sheetStyleName] ??
+ true;
}
}
diff --git a/lib/spout/src/Spout/Reader/ReaderAbstract.php b/lib/openspout/src/Reader/ReaderAbstract.php
similarity index 73%
rename from lib/spout/src/Spout/Reader/ReaderAbstract.php
rename to lib/openspout/src/Reader/ReaderAbstract.php
index 39b333d4c4eed..2b7c8b4e2443c 100644
--- a/lib/spout/src/Spout/Reader/ReaderAbstract.php
+++ b/lib/openspout/src/Reader/ReaderAbstract.php
@@ -1,19 +1,14 @@
isStreamWrapper($filePath) && (!$this->doesSupportStreamWrapper() || !$this->isSupportedStreamWrapper($filePath))) {
- throw new IOException("Could not open $filePath for reading! Stream wrapper used is not supported for this type of file.");
+ throw new IOException("Could not open {$filePath} for reading! Stream wrapper used is not supported for this type of file.");
}
if (!$this->isPhpStream($filePath)) {
// we skip the checks if the provided file path points to a PHP stream
if (!$this->globalFunctionsHelper->file_exists($filePath)) {
- throw new IOException("Could not open $filePath for reading! File does not exist.");
+ throw new IOException("Could not open {$filePath} for reading! File does not exist.");
}
if (!$this->globalFunctionsHelper->is_readable($filePath)) {
- throw new IOException("Could not open $filePath for reading! File is not readable.");
+ throw new IOException("Could not open {$filePath} for reading! File is not readable.");
}
}
@@ -127,15 +90,75 @@ public function open($filePath)
$this->openReader($fileRealPath);
$this->isStreamOpened = true;
} catch (\Exception $exception) {
- throw new IOException("Could not open $filePath for reading! ({$exception->getMessage()})");
+ throw new IOException("Could not open {$filePath} for reading! ({$exception->getMessage()})");
+ }
+ }
+
+ /**
+ * Returns an iterator to iterate over sheets.
+ *
+ * @throws \OpenSpout\Reader\Exception\ReaderNotOpenedException If called before opening the reader
+ *
+ * @return SheetIteratorInterface To iterate over sheets
+ */
+ public function getSheetIterator()
+ {
+ if (!$this->isStreamOpened) {
+ throw new ReaderNotOpenedException('Reader should be opened first.');
+ }
+
+ return $this->getConcreteSheetIterator();
+ }
+
+ /**
+ * Closes the reader, preventing any additional reading.
+ */
+ public function close()
+ {
+ if ($this->isStreamOpened) {
+ $this->closeReader();
+
+ $sheetIterator = $this->getConcreteSheetIterator();
+ if (null !== $sheetIterator) {
+ $sheetIterator->end();
+ }
+
+ $this->isStreamOpened = false;
}
}
+ /**
+ * Returns whether stream wrappers are supported.
+ *
+ * @return bool
+ */
+ abstract protected function doesSupportStreamWrapper();
+
+ /**
+ * 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
+ */
+ abstract protected function openReader($filePath);
+
+ /**
+ * Returns an iterator to iterate over sheets.
+ *
+ * @return SheetIteratorInterface To iterate over sheets
+ */
+ abstract protected function getConcreteSheetIterator();
+
+ /**
+ * Closes the reader. To be used after reading the file.
+ */
+ abstract protected function closeReader();
+
/**
* Returns the real path of the given path.
* If the given path is a valid stream wrapper, returns the path unchanged.
*
* @param string $filePath
+ *
* @return string
*/
protected function getFileRealPath($filePath)
@@ -145,7 +168,7 @@ protected function getFileRealPath($filePath)
}
// Need to use realpath to fix "Can't open file" on some Windows setup
- return \realpath($filePath);
+ return realpath($filePath);
}
/**
@@ -153,12 +176,13 @@ protected function getFileRealPath($filePath)
* For example, php://temp => php, s3://path/to/file => s3...
*
* @param string $filePath Path of the file to be read
- * @return string|null The stream wrapper scheme or NULL if not a stream wrapper
+ *
+ * @return null|string The stream wrapper scheme or NULL if not a stream wrapper
*/
protected function getStreamWrapperScheme($filePath)
{
$streamScheme = null;
- if (\preg_match('/^(\w+):\/\//', $filePath, $matches)) {
+ if (preg_match('/^(\w+):\/\//', $filePath, $matches)) {
$streamScheme = $matches[1];
}
@@ -170,11 +194,12 @@ protected function getStreamWrapperScheme($filePath)
* (like local path, php://temp, mystream://foo/bar...).
*
* @param string $filePath Path of the file to be read
+ *
* @return bool Whether the given path is an unsupported stream wrapper
*/
protected function isStreamWrapper($filePath)
{
- return ($this->getStreamWrapperScheme($filePath) !== null);
+ return null !== $this->getStreamWrapperScheme($filePath);
}
/**
@@ -183,61 +208,29 @@ protected function isStreamWrapper($filePath)
* If the given path is a local path, returns true.
*
* @param string $filePath Path of the file to be read
+ *
* @return bool Whether the given path is an supported stream wrapper
*/
protected function isSupportedStreamWrapper($filePath)
{
$streamScheme = $this->getStreamWrapperScheme($filePath);
- return ($streamScheme !== null) ?
- \in_array($streamScheme, $this->globalFunctionsHelper->stream_get_wrappers()) :
+ return (null !== $streamScheme) ?
+ \in_array($streamScheme, $this->globalFunctionsHelper->stream_get_wrappers(), true) :
true;
}
/**
- * Checks if a path is a PHP stream (like php://output, php://memory, ...)
+ * Checks if a path is a PHP stream (like php://output, php://memory, ...).
*
* @param string $filePath Path of the file to be read
+ *
* @return bool Whether the given path maps to a PHP stream
*/
protected function isPhpStream($filePath)
{
$streamScheme = $this->getStreamWrapperScheme($filePath);
- return ($streamScheme === 'php');
- }
-
- /**
- * Returns an iterator to iterate over sheets.
- *
- * @throws \Box\Spout\Reader\Exception\ReaderNotOpenedException If called before opening the reader
- * @return \Iterator To iterate over sheets
- */
- public function getSheetIterator()
- {
- if (!$this->isStreamOpened) {
- throw new ReaderNotOpenedException('Reader should be opened first.');
- }
-
- return $this->getConcreteSheetIterator();
- }
-
- /**
- * Closes the reader, preventing any additional reading
- *
- * @return void
- */
- public function close()
- {
- if ($this->isStreamOpened) {
- $this->closeReader();
-
- $sheetIterator = $this->getConcreteSheetIterator();
- if ($sheetIterator) {
- $sheetIterator->end();
- }
-
- $this->isStreamOpened = false;
- }
+ return 'php' === $streamScheme;
}
}
diff --git a/lib/spout/src/Spout/Reader/ReaderInterface.php b/lib/openspout/src/Reader/ReaderInterface.php
similarity index 52%
rename from lib/spout/src/Spout/Reader/ReaderInterface.php
rename to lib/openspout/src/Reader/ReaderInterface.php
index 74bcc4a99bd42..a1d9f74d73ed0 100644
--- a/lib/spout/src/Spout/Reader/ReaderInterface.php
+++ b/lib/openspout/src/Reader/ReaderInterface.php
@@ -1,9 +1,9 @@
initialUseInternalErrorsValue = \libxml_use_internal_errors(true);
+ libxml_clear_errors();
+ $this->initialUseInternalErrorsValue = libxml_use_internal_errors(true);
}
/**
* Throws an XMLProcessingException if an error occured.
* It also always resets the "libxml_use_internal_errors" setting back to its initial value.
*
- * @throws \Box\Spout\Reader\Exception\XMLProcessingException
- * @return void
+ * @throws \OpenSpout\Reader\Exception\XMLProcessingException
*/
protected function resetXMLInternalErrorsSettingAndThrowIfXMLErrorOccured()
{
if ($this->hasXMLErrorOccured()) {
$this->resetXMLInternalErrorsSetting();
+
throw new XMLProcessingException($this->getLastXMLErrorMessage());
}
$this->resetXMLInternalErrorsSetting();
}
+ protected function resetXMLInternalErrorsSetting()
+ {
+ libxml_use_internal_errors($this->initialUseInternalErrorsValue);
+ }
+
/**
* Returns whether the a XML error has occured since the last time errors were cleared.
*
@@ -48,32 +51,25 @@ protected function resetXMLInternalErrorsSettingAndThrowIfXMLErrorOccured()
*/
private function hasXMLErrorOccured()
{
- return (\libxml_get_last_error() !== false);
+ return false !== libxml_get_last_error();
}
/**
* Returns the error message for the last XML error that occured.
+ *
* @see libxml_get_last_error
*
- * @return string|null Last XML error message or null if no error
+ * @return null|string Last XML error message or null if no error
*/
private function getLastXMLErrorMessage()
{
$errorMessage = null;
- $error = \libxml_get_last_error();
+ $error = libxml_get_last_error();
- if ($error !== false) {
- $errorMessage = \trim($error->message);
+ if (false !== $error) {
+ $errorMessage = trim($error->message);
}
return $errorMessage;
}
-
- /**
- * @return void
- */
- protected function resetXMLInternalErrorsSetting()
- {
- \libxml_use_internal_errors($this->initialUseInternalErrorsValue);
- }
}
diff --git a/lib/spout/src/Spout/Reader/Wrapper/XMLReader.php b/lib/openspout/src/Reader/Wrapper/XMLReader.php
similarity index 75%
rename from lib/spout/src/Spout/Reader/Wrapper/XMLReader.php
rename to lib/openspout/src/Reader/Wrapper/XMLReader.php
index a05b72d85ed5d..946ca27f632cd 100644
--- a/lib/spout/src/Spout/Reader/Wrapper/XMLReader.php
+++ b/lib/openspout/src/Reader/Wrapper/XMLReader.php
@@ -1,23 +1,24 @@
open($zipFilePath) === true) {
- $doesFileExists = ($zip->locateName($innerFilePath) !== false);
- $zip->close();
- }
- }
-
- return $doesFileExists;
- }
-
- /**
- * Move to next node in document
* @see \XMLReader::read
*
- * @throws \Box\Spout\Reader\Exception\XMLProcessingException If an error/warning occurred
+ * @throws \OpenSpout\Reader\Exception\XMLProcessingException If an error/warning occurred
+ *
* @return bool TRUE on success or FALSE on failure
*/
#[\ReturnTypeWillChange]
@@ -99,7 +78,9 @@ public function read()
* Read until the element with the given name is found, or the end of the file.
*
* @param string $nodeName Name of the node to find
- * @throws \Box\Spout\Reader\Exception\XMLProcessingException If an error/warning occurred
+ *
+ * @throws \OpenSpout\Reader\Exception\XMLProcessingException If an error/warning occurred
+ *
* @return bool TRUE on success or FALSE on failure
*/
public function readUntilNodeFound($nodeName)
@@ -113,11 +94,14 @@ public function readUntilNodeFound($nodeName)
}
/**
- * Move cursor to next node skipping all subtrees
+ * Move cursor to next node skipping all subtrees.
+ *
* @see \XMLReader::next
*
- * @param string|null $localName The name of the next node to move to
- * @throws \Box\Spout\Reader\Exception\XMLProcessingException If an error/warning occurred
+ * @param null|string $localName The name of the next node to move to
+ *
+ * @throws \OpenSpout\Reader\Exception\XMLProcessingException If an error/warning occurred
+ *
* @return bool TRUE on success or FALSE on failure
*/
#[\ReturnTypeWillChange]
@@ -134,6 +118,7 @@ public function next($localName = null)
/**
* @param string $nodeName
+ *
* @return bool Whether the XML Reader is currently positioned on the starting node with given name
*/
public function isPositionedOnStartingNode($nodeName)
@@ -143,6 +128,7 @@ public function isPositionedOnStartingNode($nodeName)
/**
* @param string $nodeName
+ *
* @return bool Whether the XML Reader is currently positioned on the ending node with given name
*/
public function isPositionedOnEndingNode($nodeName)
@@ -151,26 +137,56 @@ public function isPositionedOnEndingNode($nodeName)
}
/**
- * @param string $nodeName
- * @param int $nodeType
- * @return bool Whether the XML Reader is currently positioned on the node with given name and type
+ * @return string The name of the current node, un-prefixed
*/
- private function isPositionedOnNode($nodeName, $nodeType)
+ public function getCurrentNodeName()
{
- // In some cases, the node has a prefix (for instance, "" can also be "").
- // So if the given node name does not have a prefix, we need to look at the unprefixed name ("localName").
- // @see https://github.com/box/spout/issues/233
- $hasPrefix = (\strpos($nodeName, ':') !== false);
- $currentNodeName = ($hasPrefix) ? $this->name : $this->localName;
+ return $this->localName;
+ }
- return ($this->nodeType === $nodeType && $currentNodeName === $nodeName);
+ /**
+ * Returns whether the file at the given location exists.
+ *
+ * @param string $zipStreamURI URI of a zip stream, e.g. "zip://file.zip#path/inside.xml"
+ *
+ * @return bool TRUE if the file exists, FALSE otherwise
+ */
+ protected function fileExistsWithinZip($zipStreamURI)
+ {
+ $doesFileExists = false;
+
+ $pattern = '/zip:\/\/([^#]+)#(.*)/';
+ if (preg_match($pattern, $zipStreamURI, $matches)) {
+ $zipFilePath = $matches[1];
+ $innerFilePath = $matches[2];
+
+ $zip = new \ZipArchive();
+ if (true === $zip->open($zipFilePath)) {
+ $doesFileExists = (false !== $zip->locateName($innerFilePath));
+ $zip->close();
+ }
+ }
+
+ return $doesFileExists;
}
/**
- * @return string The name of the current node, un-prefixed
+ * @param string $nodeName
+ * @param int $nodeType
+ *
+ * @return bool Whether the XML Reader is currently positioned on the node with given name and type
*/
- public function getCurrentNodeName()
+ private function isPositionedOnNode($nodeName, $nodeType)
{
- return $this->localName;
+ /**
+ * In some cases, the node has a prefix (for instance, "" can also be "").
+ * So if the given node name does not have a prefix, we need to look at the unprefixed name ("localName").
+ *
+ * @see https://github.com/box/spout/issues/233
+ */
+ $hasPrefix = (false !== strpos($nodeName, ':'));
+ $currentNodeName = ($hasPrefix) ? $this->name : $this->localName;
+
+ return $this->nodeType === $nodeType && $currentNodeName === $nodeName;
}
}
diff --git a/lib/openspout/src/Reader/XLSX/Creator/HelperFactory.php b/lib/openspout/src/Reader/XLSX/Creator/HelperFactory.php
new file mode 100644
index 0000000000000..3528b23e6e83d
--- /dev/null
+++ b/lib/openspout/src/Reader/XLSX/Creator/HelperFactory.php
@@ -0,0 +1,38 @@
+createStringsEscaper();
+
+ return new CellValueFormatter($sharedStringsManager, $styleManager, $shouldFormatDates, $shouldUse1904Dates, $escaper);
+ }
+
+ /**
+ * @return Escaper\XLSX
+ */
+ public function createStringsEscaper()
+ {
+ // @noinspection PhpUnnecessaryFullyQualifiedNameInspection
+ return new Escaper\XLSX();
+ }
+}
diff --git a/lib/spout/src/Spout/Reader/XLSX/Creator/InternalEntityFactory.php b/lib/openspout/src/Reader/XLSX/Creator/InternalEntityFactory.php
similarity index 57%
rename from lib/spout/src/Spout/Reader/XLSX/Creator/InternalEntityFactory.php
rename to lib/openspout/src/Reader/XLSX/Creator/InternalEntityFactory.php
index 4dc2dc7d94240..33b18df6fb3b7 100644
--- a/lib/spout/src/Spout/Reader/XLSX/Creator/InternalEntityFactory.php
+++ b/lib/openspout/src/Reader/XLSX/Creator/InternalEntityFactory.php
@@ -1,21 +1,20 @@
managerFactory = $managerFactory;
@@ -36,9 +31,10 @@ public function __construct(ManagerFactory $managerFactory, HelperFactory $helpe
}
/**
- * @param string $filePath Path of the file to be read
- * @param \Box\Spout\Common\Manager\OptionsManagerInterface $optionsManager Reader's options manager
- * @param SharedStringsManager $sharedStringsManager Manages shared strings
+ * @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)
@@ -54,14 +50,15 @@ public function createSheetIterator($filePath, $optionsManager, $sharedStringsMa
}
/**
- * @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 \Box\Spout\Common\Manager\OptionsManagerInterface $optionsManager Reader's options manager
- * @param SharedStringsManager $sharedStringsManager Manages shared strings
+ * @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(
@@ -79,46 +76,9 @@ public function createSheet(
return new Sheet($rowIterator, $sheetIndex, $sheetName, $isSheetActive, $isSheetVisible);
}
- /**
- * @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 \Box\Spout\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
- );
- }
-
/**
* @param Cell[] $cells
+ *
* @return Row
*/
public function createRow(array $cells = [])
@@ -128,6 +88,7 @@ public function createRow(array $cells = [])
/**
* @param mixed $cellValue
+ *
* @return Cell
*/
public function createCell($cellValue)
@@ -152,11 +113,51 @@ public function createXMLReader()
}
/**
- * @param $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
+ );
+ }
}
diff --git a/lib/spout/src/Spout/Reader/XLSX/Creator/ManagerFactory.php b/lib/openspout/src/Reader/XLSX/Creator/ManagerFactory.php
similarity index 66%
rename from lib/spout/src/Spout/Reader/XLSX/Creator/ManagerFactory.php
rename to lib/openspout/src/Reader/XLSX/Creator/ManagerFactory.php
index d23a53f3ca2d4..10a8833a4e916 100644
--- a/lib/spout/src/Spout/Reader/XLSX/Creator/ManagerFactory.php
+++ b/lib/openspout/src/Reader/XLSX/Creator/ManagerFactory.php
@@ -1,17 +1,16 @@
cachedWorkbookRelationshipsManager)) {
- $this->cachedWorkbookRelationshipsManager = new WorkbookRelationshipsManager($filePath, $entityFactory);
- }
-
- return $this->cachedWorkbookRelationshipsManager;
- }
-
- /**
- * @param string $filePath Path of the XLSX file being read
- * @param \Box\Spout\Common\Manager\OptionsManagerInterface $optionsManager Reader's options manager
- * @param \Box\Spout\Reader\XLSX\Manager\SharedStringsManager $sharedStringsManager Manages shared strings
- * @param InternalEntityFactory $entityFactory Factory to create entities
+ * @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)
@@ -83,8 +70,9 @@ public function createSheetManager($filePath, $optionsManager, $sharedStringsMan
}
/**
- * @param string $filePath Path of the XLSX file being read
+ * @param string $filePath Path of the XLSX file being read
* @param InternalEntityFactory $entityFactory Factory to create entities
+ *
* @return StyleManager
*/
public function createStyleManager($filePath, $entityFactory)
@@ -96,10 +84,26 @@ public function createStyleManager($filePath, $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;
+ }
}
diff --git a/lib/spout/src/Spout/Reader/XLSX/Helper/CellHelper.php b/lib/openspout/src/Reader/XLSX/Helper/CellHelper.php
similarity index 88%
rename from lib/spout/src/Spout/Reader/XLSX/Helper/CellHelper.php
rename to lib/openspout/src/Reader/XLSX/Helper/CellHelper.php
index d970189e65327..827d728ba57e7 100644
--- a/lib/spout/src/Spout/Reader/XLSX/Helper/CellHelper.php
+++ b/lib/openspout/src/Reader/XLSX/Helper/CellHelper.php
@@ -1,12 +1,11 @@
getAttribute(self::XML_ATTRIBUTE_STYLE_ID);
$vNodeValue = $this->getVNodeValue($node);
- if (($vNodeValue === '') && ($cellType !== self::CELL_TYPE_INLINE_STRING)) {
+ if (('' === $vNodeValue) && (self::CELL_TYPE_INLINE_STRING !== $cellType)) {
return $vNodeValue;
}
switch ($cellType) {
case self::CELL_TYPE_INLINE_STRING:
return $this->formatInlineStringCellValue($node);
+
case self::CELL_TYPE_SHARED_STRING:
return $this->formatSharedStringCellValue($vNodeValue);
+
case self::CELL_TYPE_STR:
return $this->formatStrCellValue($vNodeValue);
+
case self::CELL_TYPE_BOOLEAN:
return $this->formatBooleanCellValue($vNodeValue);
+
case self::CELL_TYPE_NUMERIC:
return $this->formatNumericCellValue($vNodeValue, $cellStyleId);
+
case self::CELL_TYPE_DATE:
return $this->formatDateCellValue($vNodeValue);
+
default:
throw new InvalidValueException($vNodeValue);
}
}
/**
- * Returns the cell's string value from a node's nested value node
+ * Returns the cell's string value from a node's nested value node.
+ *
+ * @param \DOMElement $node
*
- * @param \DOMNode $node
* @return string The value associated with the cell
*/
protected function getVNodeValue($node)
@@ -111,13 +119,14 @@ protected function getVNodeValue($node)
// if not, the returned value should be empty string.
$vNode = $node->getElementsByTagName(self::XML_NODE_VALUE)->item(0);
- return ($vNode !== null) ? $vNode->nodeValue : '';
+ return (null !== $vNode) ? $vNode->nodeValue : '';
}
/**
* Returns the cell String value where string is inline.
*
- * @param \DOMNode $node
+ * @param \DOMElement $node
+ *
* @return string The value associated with the cell
*/
protected function formatInlineStringCellValue($node)
@@ -127,7 +136,7 @@ protected function formatInlineStringCellValue($node)
$tNodes = $node->getElementsByTagName(self::XML_NODE_INLINE_STRING_VALUE);
$cellValue = '';
- for ($i = 0; $i < $tNodes->count(); $i++) {
+ for ($i = 0; $i < $tNodes->count(); ++$i) {
$tNode = $tNodes->item($i);
$cellValue .= $this->escaper->unescape($tNode->nodeValue);
}
@@ -139,6 +148,7 @@ protected function formatInlineStringCellValue($node)
* Returns the cell String value from shared-strings file using nodeValue index.
*
* @param string $nodeValue
+ *
* @return string The value associated with the cell
*/
protected function formatSharedStringCellValue($nodeValue)
@@ -147,23 +157,22 @@ protected function formatSharedStringCellValue($nodeValue)
// [SHARED_STRING_INDEX]
$sharedStringIndex = (int) $nodeValue;
$escapedCellValue = $this->sharedStringsManager->getStringAtIndex($sharedStringIndex);
- $cellValue = $this->escaper->unescape($escapedCellValue);
- return $cellValue;
+ return $this->escaper->unescape($escapedCellValue);
}
/**
* Returns the cell String value, where string is stored in value node.
*
* @param string $nodeValue
+ *
* @return string The value associated with the cell
*/
protected function formatStrCellValue($nodeValue)
{
- $escapedCellValue = \trim($nodeValue);
- $cellValue = $this->escaper->unescape($escapedCellValue);
+ $escapedCellValue = trim($nodeValue);
- return $cellValue;
+ return $this->escaper->unescape($escapedCellValue);
}
/**
@@ -171,8 +180,9 @@ protected function formatStrCellValue($nodeValue)
* The value can also represent a timestamp and a DateTime will be returned.
*
* @param string $nodeValue
- * @param int $cellStyleId 0 being the default style
- * @return int|float|\DateTime The value associated with the cell
+ * @param int $cellStyleId 0 being the default style
+ *
+ * @return \DateTime|float|int The value associated with the cell
*/
protected function formatNumericCellValue($nodeValue, $cellStyleId)
{
@@ -200,8 +210,10 @@ protected function formatNumericCellValue($nodeValue, $cellStyleId)
* @see ECMA-376 Part 1 - §18.17.4
*
* @param float $nodeValue
- * @param int $cellStyleId 0 being the default style
+ * @param int $cellStyleId 0 being the default style
+ *
* @throws InvalidValueException If the value is not a valid timestamp
+ *
* @return \DateTime The value associated with the cell
*/
protected function formatExcelTimestampValue($nodeValue, $cellStyleId)
@@ -216,19 +228,21 @@ protected function formatExcelTimestampValue($nodeValue, $cellStyleId)
}
/**
- * Returns whether the given timestamp is supported by SpreadsheetML
+ * Returns whether the given timestamp is supported by SpreadsheetML.
+ *
* @see ECMA-376 Part 1 - §18.17.4 - this specifies the timestamp boundaries.
*
* @param float $timestampValue
+ *
* @return bool
*/
protected function isValidTimestampValue($timestampValue)
{
// @NOTE: some versions of Excel don't support negative dates (e.g. Excel for Mac 2011)
- return (
- $this->shouldUse1904Dates && $timestampValue >= -695055 && $timestampValue <= 2957003.9999884 ||
- !$this->shouldUse1904Dates && $timestampValue >= -693593 && $timestampValue <= 2958465.9999884
- );
+ return
+ $this->shouldUse1904Dates && $timestampValue >= -695055 && $timestampValue <= 2957003.9999884
+ || !$this->shouldUse1904Dates && $timestampValue >= -693593 && $timestampValue <= 2958465.9999884
+ ;
}
/**
@@ -237,7 +251,8 @@ protected function isValidTimestampValue($timestampValue)
* Dec 30th 1899, 1900 or Jan 1st, 1904, depending on the Workbook setting.
*
* @param float $nodeValue
- * @param int $cellStyleId 0 being the default style
+ * @param int $cellStyleId 0 being the default style
+ *
* @return \DateTime|string The value associated with the cell
*/
protected function formatExcelTimestampValueAsDateTimeValue($nodeValue, $cellStyleId)
@@ -245,12 +260,12 @@ protected function formatExcelTimestampValueAsDateTimeValue($nodeValue, $cellSty
$baseDate = $this->shouldUse1904Dates ? '1904-01-01' : '1899-12-30';
$daysSinceBaseDate = (int) $nodeValue;
- $timeRemainder = \fmod($nodeValue, 1);
- $secondsRemainder = \round($timeRemainder * self::NUM_SECONDS_IN_ONE_DAY, 0);
+ $timeRemainder = fmod($nodeValue, 1);
+ $secondsRemainder = round($timeRemainder * self::NUM_SECONDS_IN_ONE_DAY, 0);
$dateObj = \DateTime::createFromFormat('|Y-m-d', $baseDate);
- $dateObj->modify('+' . $daysSinceBaseDate . 'days');
- $dateObj->modify('+' . $secondsRemainder . 'seconds');
+ $dateObj->modify('+'.$daysSinceBaseDate.'days');
+ $dateObj->modify('+'.$secondsRemainder.'seconds');
if ($this->shouldFormatDates) {
$styleNumberFormatCode = $this->styleManager->getNumberFormatCode($cellStyleId);
@@ -267,6 +282,7 @@ protected function formatExcelTimestampValueAsDateTimeValue($nodeValue, $cellSty
* Returns the cell Boolean value from a specific node's Value.
*
* @param string $nodeValue
+ *
* @return bool The value associated with the cell
*/
protected function formatBooleanCellValue($nodeValue)
@@ -276,10 +292,13 @@ protected function formatBooleanCellValue($nodeValue)
/**
* Returns a cell's PHP Date value, associated to the given stored nodeValue.
+ *
* @see ECMA-376 Part 1 - §18.17.4
*
* @param string $nodeValue ISO 8601 Date string
+ *
* @throws InvalidValueException If the value is not a valid date
+ *
* @return \DateTime|string The value associated with the cell
*/
protected function formatDateCellValue($nodeValue)
diff --git a/lib/spout/src/Spout/Reader/XLSX/Helper/DateFormatHelper.php b/lib/openspout/src/Reader/XLSX/Helper/DateFormatHelper.php
similarity index 51%
rename from lib/spout/src/Spout/Reader/XLSX/Helper/DateFormatHelper.php
rename to lib/openspout/src/Reader/XLSX/Helper/DateFormatHelper.php
index 443578819412d..78fc4f43cb2bf 100644
--- a/lib/spout/src/Spout/Reader/XLSX/Helper/DateFormatHelper.php
+++ b/lib/openspout/src/Reader/XLSX/Helper/DateFormatHelper.php
@@ -1,16 +1,15 @@
[
// Time
'am/pm' => 'A', // Uppercase Ante meridiem and Post meridiem
- ':mm' => ':i', // Minutes with leading zeros - if preceded by a ":" (otherwise month)
- 'mm:' => 'i:', // Minutes with leading zeros - if followed by a ":" (otherwise month)
- 'ss' => 's', // Seconds, with leading zeros
- '.s' => '', // Ignore (fractional seconds format does not exist in PHP)
+ ':mm' => ':i', // Minutes with leading zeros - if preceded by a ":" (otherwise month)
+ 'mm:' => 'i:', // Minutes with leading zeros - if followed by a ":" (otherwise month)
+ 'ss' => 's', // Seconds, with leading zeros
+ '.s' => '', // Ignore (fractional seconds format does not exist in PHP)
// Date
- 'e' => 'Y', // Full numeric representation of a year, 4 digits
- 'yyyy' => 'Y', // Full numeric representation of a year, 4 digits
- 'yy' => 'y', // Two digit representation of a year
+ 'e' => 'Y', // Full numeric representation of a year, 4 digits
+ 'yyyy' => 'Y', // Full numeric representation of a year, 4 digits
+ 'yy' => 'y', // Two digit representation of a year
'mmmmm' => 'M', // Short textual representation of a month, three letters ("mmmmm" should only contain the 1st letter...)
- 'mmmm' => 'F', // Full textual representation of a month
- 'mmm' => 'M', // Short textual representation of a month, three letters
- 'mm' => 'm', // Numeric representation of a month, with leading zeros
- 'm' => 'n', // Numeric representation of a month, without leading zeros
- 'dddd' => 'l', // Full textual representation of the day of the week
- 'ddd' => 'D', // Textual representation of a day, three letters
- 'dd' => 'd', // Day of the month, 2 digits with leading zeros
- 'd' => 'j', // Day of the month without leading zeros
+ 'mmmm' => 'F', // Full textual representation of a month
+ 'mmm' => 'M', // Short textual representation of a month, three letters
+ 'mm' => 'm', // Numeric representation of a month, with leading zeros
+ 'm' => 'n', // Numeric representation of a month, without leading zeros
+ 'dddd' => 'l', // Full textual representation of the day of the week
+ 'ddd' => 'D', // Textual representation of a day, three letters
+ 'dd' => 'd', // Day of the month, 2 digits with leading zeros
+ 'd' => 'j', // Day of the month without leading zeros
],
self::KEY_HOUR_12 => [
- 'hh' => 'h', // 12-hour format of an hour without leading zeros
- 'h' => 'g', // 12-hour format of an hour without leading zeros
+ 'hh' => 'h', // 12-hour format of an hour without leading zeros
+ 'h' => 'g', // 12-hour format of an hour without leading zeros
],
self::KEY_HOUR_24 => [
- 'hh' => 'H', // 24-hour hours with leading zero
- 'h' => 'G', // 24-hour format of an hour without leading zeros
+ 'hh' => 'H', // 24-hour hours with leading zero
+ 'h' => 'G', // 24-hour format of an hour without leading zeros
],
];
@@ -55,6 +54,7 @@ class DateFormatHelper
* Converts the given Excel date format to a format understandable by the PHP date function.
*
* @param string $excelDateFormat Excel date format
+ *
* @return string PHP date format (as defined here: http://php.net/manual/en/function.date.php)
*/
public static function toPHPDateFormat($excelDateFormat)
@@ -62,34 +62,34 @@ public static function toPHPDateFormat($excelDateFormat)
// Remove brackets potentially present at the beginning of the format string
// and text portion of the format at the end of it (starting with ";")
// See §18.8.31 of ECMA-376 for more detail.
- $dateFormat = \preg_replace('/^(?:\[\$[^\]]+?\])?([^;]*).*/', '$1', $excelDateFormat);
+ $dateFormat = preg_replace('/^(?:\[\$[^\]]+?\])?([^;]*).*/', '$1', $excelDateFormat);
// Double quotes are used to escape characters that must not be interpreted.
// For instance, ["Day " dd] should result in "Day 13" and we should not try to interpret "D", "a", "y"
// By exploding the format string using double quote as a delimiter, we can get all parts
// that must be transformed (even indexes) and all parts that must not be (odd indexes).
- $dateFormatParts = \explode('"', $dateFormat);
+ $dateFormatParts = explode('"', $dateFormat);
foreach ($dateFormatParts as $partIndex => $dateFormatPart) {
// do not look at odd indexes
- if ($partIndex % 2 === 1) {
+ if (1 === $partIndex % 2) {
continue;
}
// Make sure all characters are lowercase, as the mapping table is using lowercase characters
- $transformedPart = \strtolower($dateFormatPart);
+ $transformedPart = strtolower($dateFormatPart);
// Remove escapes related to non-format characters
- $transformedPart = \str_replace('\\', '', $transformedPart);
+ $transformedPart = str_replace('\\', '', $transformedPart);
// Apply general transformation first...
- $transformedPart = \strtr($transformedPart, self::$excelDateFormatToPHPDateFormatMapping[self::KEY_GENERAL]);
+ $transformedPart = strtr($transformedPart, self::$excelDateFormatToPHPDateFormatMapping[self::KEY_GENERAL]);
// ... then apply hour transformation, for 12-hour or 24-hour format
if (self::has12HourFormatMarker($dateFormatPart)) {
- $transformedPart = \strtr($transformedPart, self::$excelDateFormatToPHPDateFormatMapping[self::KEY_HOUR_12]);
+ $transformedPart = strtr($transformedPart, self::$excelDateFormatToPHPDateFormatMapping[self::KEY_HOUR_12]);
} else {
- $transformedPart = \strtr($transformedPart, self::$excelDateFormatToPHPDateFormatMapping[self::KEY_HOUR_24]);
+ $transformedPart = strtr($transformedPart, self::$excelDateFormatToPHPDateFormatMapping[self::KEY_HOUR_24]);
}
// overwrite the parts array with the new transformed part
@@ -97,27 +97,26 @@ public static function toPHPDateFormat($excelDateFormat)
}
// Merge all transformed parts back together
- $phpDateFormat = \implode('"', $dateFormatParts);
+ $phpDateFormat = implode('"', $dateFormatParts);
// Finally, to have the date format compatible with the DateTime::format() function, we need to escape
// all characters that are inside double quotes (and double quotes must be removed).
// For instance, ["Day " dd] should become [\D\a\y\ dd]
- $phpDateFormat = \preg_replace_callback('/"(.+?)"/', function ($matches) {
+ return preg_replace_callback('/"(.+?)"/', function ($matches) {
$stringToEscape = $matches[1];
- $letters = \preg_split('//u', $stringToEscape, -1, PREG_SPLIT_NO_EMPTY);
+ $letters = preg_split('//u', $stringToEscape, -1, PREG_SPLIT_NO_EMPTY);
- return '\\' . \implode('\\', $letters);
+ return '\\'.implode('\\', $letters);
}, $phpDateFormat);
-
- return $phpDateFormat;
}
/**
* @param string $excelDateFormat Date format as defined by Excel
+ *
* @return bool Whether the given date format has the 12-hour format marker
*/
private static function has12HourFormatMarker($excelDateFormat)
{
- return (\stripos($excelDateFormat, 'am/pm') !== false);
+ return false !== stripos($excelDateFormat, 'am/pm');
}
}
diff --git a/lib/spout/src/Spout/Reader/XLSX/Manager/OptionsManager.php b/lib/openspout/src/Reader/XLSX/Manager/OptionsManager.php
similarity index 71%
rename from lib/spout/src/Spout/Reader/XLSX/Manager/OptionsManager.php
rename to lib/openspout/src/Reader/XLSX/Manager/OptionsManager.php
index 25fcc5cbb02f1..b04b92c816b3f 100644
--- a/lib/spout/src/Spout/Reader/XLSX/Manager/OptionsManager.php
+++ b/lib/openspout/src/Reader/XLSX/Manager/OptionsManager.php
@@ -1,13 +1,12 @@
setOption(Options::TEMP_FOLDER, \sys_get_temp_dir());
+ $this->setOption(Options::TEMP_FOLDER, sys_get_temp_dir());
$this->setOption(Options::SHOULD_FORMAT_DATES, false);
$this->setOption(Options::SHOULD_PRESERVE_EMPTY_ROWS, false);
$this->setOption(Options::SHOULD_USE_1904_DATES, false);
diff --git a/lib/spout/src/Spout/Reader/XLSX/Manager/SharedStringsCaching/CachingStrategyFactory.php b/lib/openspout/src/Reader/XLSX/Manager/SharedStringsCaching/CachingStrategyFactory.php
similarity index 76%
rename from lib/spout/src/Spout/Reader/XLSX/Manager/SharedStringsCaching/CachingStrategyFactory.php
rename to lib/openspout/src/Reader/XLSX/Manager/SharedStringsCaching/CachingStrategyFactory.php
index 1c00a52d6c3b4..c2f8c9fd52fae 100644
--- a/lib/spout/src/Spout/Reader/XLSX/Manager/SharedStringsCaching/CachingStrategyFactory.php
+++ b/lib/openspout/src/Reader/XLSX/Manager/SharedStringsCaching/CachingStrategyFactory.php
@@ -1,16 +1,13 @@
20 * 600 ≈ 12KB
*/
- const AMOUNT_MEMORY_NEEDED_PER_STRING_IN_KB = 12;
+ public const AMOUNT_MEMORY_NEEDED_PER_STRING_IN_KB = 12;
/**
* To avoid running out of memory when extracting a huge number of shared strings, they can be saved to temporary files
@@ -48,15 +45,16 @@ class CachingStrategyFactory
* best when the indexes of the shared strings are sorted in the sheet data.
* 10,000 was chosen because it creates small files that are fast to be loaded in memory.
*/
- const MAX_NUM_STRINGS_PER_TEMP_FILE = 10000;
+ public const MAX_NUM_STRINGS_PER_TEMP_FILE = 10000;
/**
* Returns the best caching strategy, given the number of unique shared strings
* and the amount of memory available.
*
- * @param int|null $sharedStringsUniqueCount Number of unique shared strings (NULL if unknown)
- * @param string $tempFolder Temporary folder where the temporary files to store shared strings will be stored
- * @param HelperFactory $helperFactory Factory to create helpers
+ * @param null|int $sharedStringsUniqueCount Number of unique shared strings (NULL if unknown)
+ * @param string $tempFolder Temporary folder where the temporary files to store shared strings will be stored
+ * @param HelperFactory $helperFactory Factory to create helpers
+ *
* @return CachingStrategyInterface The best caching strategy
*/
public function createBestCachingStrategy($sharedStringsUniqueCount, $tempFolder, $helperFactory)
@@ -72,19 +70,20 @@ public function createBestCachingStrategy($sharedStringsUniqueCount, $tempFolder
* Returns whether it is safe to use in-memory caching, given the number of unique shared strings
* and the amount of memory available.
*
- * @param int|null $sharedStringsUniqueCount Number of unique shared strings (NULL if unknown)
+ * @param null|int $sharedStringsUniqueCount Number of unique shared strings (NULL if unknown)
+ *
* @return bool
*/
protected function isInMemoryStrategyUsageSafe($sharedStringsUniqueCount)
{
// if the number of shared strings in unknown, do not use "in memory" strategy
- if ($sharedStringsUniqueCount === null) {
+ if (null === $sharedStringsUniqueCount) {
return false;
}
$memoryAvailable = $this->getMemoryLimitInKB();
- if ($memoryAvailable === -1) {
+ if (-1 === (int) $memoryAvailable) {
// if cannot get memory limit or if memory limit set as unlimited, don't trust and play safe
$isInMemoryStrategyUsageSafe = ($sharedStringsUniqueCount < self::MAX_NUM_STRINGS_PER_TEMP_FILE);
} else {
@@ -96,30 +95,34 @@ protected function isInMemoryStrategyUsageSafe($sharedStringsUniqueCount)
}
/**
- * Returns the PHP "memory_limit" in Kilobytes
+ * Returns the PHP "memory_limit" in Kilobytes.
*
* @return float
*/
protected function getMemoryLimitInKB()
{
$memoryLimitFormatted = $this->getMemoryLimitFromIni();
- $memoryLimitFormatted = \strtolower(\trim($memoryLimitFormatted));
+ $memoryLimitFormatted = strtolower(trim($memoryLimitFormatted));
// No memory limit
- if ($memoryLimitFormatted === '-1') {
+ if ('-1' === $memoryLimitFormatted) {
return -1;
}
- if (\preg_match('/(\d+)([bkmgt])b?/', $memoryLimitFormatted, $matches)) {
+ if (preg_match('/(\d+)([bkmgt])b?/', $memoryLimitFormatted, $matches)) {
$amount = (int) ($matches[1]);
$unit = $matches[2];
switch ($unit) {
- case 'b': return ($amount / 1024);
+ case 'b': return $amount / 1024;
+
case 'k': return $amount;
- case 'm': return ($amount * 1024);
- case 'g': return ($amount * 1024 * 1024);
- case 't': return ($amount * 1024 * 1024 * 1024);
+
+ case 'm': return $amount * 1024;
+
+ case 'g': return $amount * 1024 * 1024;
+
+ case 't': return $amount * 1024 * 1024 * 1024;
}
}
@@ -127,12 +130,12 @@ protected function getMemoryLimitInKB()
}
/**
- * Returns the formatted "memory_limit" value
+ * Returns the formatted "memory_limit" value.
*
* @return string
*/
protected function getMemoryLimitFromIni()
{
- return \ini_get('memory_limit');
+ return ini_get('memory_limit');
}
}
diff --git a/lib/spout/src/Spout/Reader/XLSX/Manager/SharedStringsCaching/CachingStrategyInterface.php b/lib/openspout/src/Reader/XLSX/Manager/SharedStringsCaching/CachingStrategyInterface.php
similarity index 68%
rename from lib/spout/src/Spout/Reader/XLSX/Manager/SharedStringsCaching/CachingStrategyInterface.php
rename to lib/openspout/src/Reader/XLSX/Manager/SharedStringsCaching/CachingStrategyInterface.php
index 479b0b535c0ca..b3148274e3ad3 100644
--- a/lib/spout/src/Spout/Reader/XLSX/Manager/SharedStringsCaching/CachingStrategyInterface.php
+++ b/lib/openspout/src/Reader/XLSX/Manager/SharedStringsCaching/CachingStrategyInterface.php
@@ -1,26 +1,23 @@
fileSystemHelper = $helperFactory->createFileSystemHelper($tempFolder);
- $this->tempFolder = $this->fileSystemHelper->createFolder($tempFolder, \uniqid('sharedstrings'));
+ $this->tempFolder = $this->fileSystemHelper->createFolder($tempFolder, uniqid('sharedstrings'));
$this->maxNumStringsPerTempFile = $maxNumStringsPerTempFile;
@@ -66,9 +67,8 @@ public function __construct($tempFolder, $maxNumStringsPerTempFile, $helperFacto
/**
* Adds the given string to the cache.
*
- * @param string $sharedString The string to be added to the cache
- * @param int $sharedStringIndex Index of the shared string in the sharedStrings.xml file
- * @return void
+ * @param string $sharedString The string to be added to the cache
+ * @param int $sharedStringIndex Index of the shared string in the sharedStrings.xml file
*/
public function addStringForIndex($sharedString, $sharedStringIndex)
{
@@ -85,27 +85,12 @@ public function addStringForIndex($sharedString, $sharedStringIndex)
// Encoding the line feed character allows to preserve this assumption
$lineFeedEncodedSharedString = $this->escapeLineFeed($sharedString);
- $this->globalFunctionsHelper->fwrite($this->tempFilePointer, $lineFeedEncodedSharedString . PHP_EOL);
- }
-
- /**
- * Returns the path for the temp file that should contain the string for the given index
- *
- * @param int $sharedStringIndex Index of the shared string in the sharedStrings.xml file
- * @return string The temp file path for the given index
- */
- protected function getSharedStringTempFilePath($sharedStringIndex)
- {
- $numTempFile = (int) ($sharedStringIndex / $this->maxNumStringsPerTempFile);
-
- return $this->tempFolder . '/sharedstrings' . $numTempFile;
+ $this->globalFunctionsHelper->fwrite($this->tempFilePointer, $lineFeedEncodedSharedString.PHP_EOL);
}
/**
* Closes the cache after the last shared string was added.
* This prevents any additional string from being added to the cache.
- *
- * @return void
*/
public function closeCache()
{
@@ -119,7 +104,9 @@ public function closeCache()
* Returns the string located at the given index from the cache.
*
* @param int $sharedStringIndex Index of the shared string in the sharedStrings.xml file
- * @throws \Box\Spout\Reader\Exception\SharedStringNotFoundException If no shared string found for the given index
+ *
+ * @throws \OpenSpout\Reader\Exception\SharedStringNotFoundException If no shared string found for the given index
+ *
* @return string The shared string at the given index
*/
public function getStringAtIndex($sharedStringIndex)
@@ -128,14 +115,11 @@ public function getStringAtIndex($sharedStringIndex)
$indexInFile = $sharedStringIndex % $this->maxNumStringsPerTempFile;
if (!$this->globalFunctionsHelper->file_exists($tempFilePath)) {
- throw new SharedStringNotFoundException("Shared string temp file not found: $tempFilePath ; for index: $sharedStringIndex");
+ throw new SharedStringNotFoundException("Shared string temp file not found: {$tempFilePath} ; for index: {$sharedStringIndex}");
}
if ($this->inMemoryTempFilePath !== $tempFilePath) {
- // free memory
- unset($this->inMemoryTempFileContents);
-
- $this->inMemoryTempFileContents = \explode(PHP_EOL, $this->globalFunctionsHelper->file_get_contents($tempFilePath));
+ $this->inMemoryTempFileContents = explode(PHP_EOL, $this->globalFunctionsHelper->file_get_contents($tempFilePath));
$this->inMemoryTempFilePath = $tempFilePath;
}
@@ -147,44 +131,58 @@ public function getStringAtIndex($sharedStringIndex)
$sharedString = $this->unescapeLineFeed($escapedSharedString);
}
- if ($sharedString === null) {
- throw new SharedStringNotFoundException("Shared string not found for index: $sharedStringIndex");
+ if (null === $sharedString) {
+ throw new SharedStringNotFoundException("Shared string not found for index: {$sharedStringIndex}");
}
- return \rtrim($sharedString, PHP_EOL);
+ return rtrim($sharedString, PHP_EOL);
+ }
+
+ /**
+ * Destroys the cache, freeing memory and removing any created artifacts.
+ */
+ public function clearCache()
+ {
+ if ($this->tempFolder) {
+ $this->fileSystemHelper->deleteFolderRecursively($this->tempFolder);
+ }
}
/**
- * Escapes the line feed characters (\n)
+ * Returns the path for the temp file that should contain the string for the given index.
*
- * @param string $unescapedString
- * @return string
+ * @param int $sharedStringIndex Index of the shared string in the sharedStrings.xml file
+ *
+ * @return string The temp file path for the given index
*/
- private function escapeLineFeed($unescapedString)
+ protected function getSharedStringTempFilePath($sharedStringIndex)
{
- return \str_replace("\n", self::ESCAPED_LINE_FEED_CHARACTER, $unescapedString);
+ $numTempFile = (int) ($sharedStringIndex / $this->maxNumStringsPerTempFile);
+
+ return $this->tempFolder.'/sharedstrings'.$numTempFile;
}
/**
- * Unescapes the line feed characters (\n)
+ * Escapes the line feed characters (\n).
+ *
+ * @param string $unescapedString
*
- * @param string $escapedString
* @return string
*/
- private function unescapeLineFeed($escapedString)
+ private function escapeLineFeed($unescapedString)
{
- return \str_replace(self::ESCAPED_LINE_FEED_CHARACTER, "\n", $escapedString);
+ return str_replace("\n", self::ESCAPED_LINE_FEED_CHARACTER, $unescapedString);
}
/**
- * Destroys the cache, freeing memory and removing any created artifacts
+ * Unescapes the line feed characters (\n).
+ *
+ * @param string $escapedString
*
- * @return void
+ * @return string
*/
- public function clearCache()
+ private function unescapeLineFeed($escapedString)
{
- if ($this->tempFolder) {
- $this->fileSystemHelper->deleteFolderRecursively($this->tempFolder);
- }
+ return str_replace(self::ESCAPED_LINE_FEED_CHARACTER, "\n", $escapedString);
}
}
diff --git a/lib/spout/src/Spout/Reader/XLSX/Manager/SharedStringsCaching/InMemoryStrategy.php b/lib/openspout/src/Reader/XLSX/Manager/SharedStringsCaching/InMemoryStrategy.php
similarity index 78%
rename from lib/spout/src/Spout/Reader/XLSX/Manager/SharedStringsCaching/InMemoryStrategy.php
rename to lib/openspout/src/Reader/XLSX/Manager/SharedStringsCaching/InMemoryStrategy.php
index a16d4de5092f7..04312ad21f57a 100644
--- a/lib/spout/src/Spout/Reader/XLSX/Manager/SharedStringsCaching/InMemoryStrategy.php
+++ b/lib/openspout/src/Reader/XLSX/Manager/SharedStringsCaching/InMemoryStrategy.php
@@ -1,12 +1,10 @@
inMemoryCache->offsetGet($sharedStringIndex);
} catch (\RuntimeException $e) {
- throw new SharedStringNotFoundException("Shared string not found for index: $sharedStringIndex");
+ throw new SharedStringNotFoundException("Shared string not found for index: {$sharedStringIndex}");
}
}
/**
- * Destroys the cache, freeing memory and removing any created artifacts
- *
- * @return void
+ * Destroys the cache, freeing memory and removing any created artifacts.
*/
public function clearCache()
{
- unset($this->inMemoryCache);
+ $this->inMemoryCache = new \SplFixedArray(0);
$this->isCacheClosed = false;
}
}
diff --git a/lib/spout/src/Spout/Reader/XLSX/Manager/SharedStringsManager.php b/lib/openspout/src/Reader/XLSX/Manager/SharedStringsManager.php
similarity index 70%
rename from lib/spout/src/Spout/Reader/XLSX/Manager/SharedStringsManager.php
rename to lib/openspout/src/Reader/XLSX/Manager/SharedStringsManager.php
index 81b4ba29936ae..120ce6871c346 100644
--- a/lib/spout/src/Spout/Reader/XLSX/Manager/SharedStringsManager.php
+++ b/lib/openspout/src/Reader/XLSX/Manager/SharedStringsManager.php
@@ -1,32 +1,31 @@
entityFactory->createXMLReader();
$sharedStringIndex = 0;
- if ($xmlReader->openFileInZip($this->filePath, $sharedStringsXMLFilePath) === false) {
- throw new IOException('Could not open "' . $sharedStringsXMLFilePath . '".');
+ if (false === $xmlReader->openFileInZip($this->filePath, $sharedStringsXMLFilePath)) {
+ throw new IOException('Could not open "'.$sharedStringsXMLFilePath.'".');
}
try {
@@ -112,9 +110,9 @@ public function extractSharedStrings()
$xmlReader->readUntilNodeFound(self::XML_NODE_SI);
- while ($xmlReader->getCurrentNodeName() === self::XML_NODE_SI) {
+ while (self::XML_NODE_SI === $xmlReader->getCurrentNodeName()) {
$this->processSharedStringsItem($xmlReader, $sharedStringIndex);
- $sharedStringIndex++;
+ ++$sharedStringIndex;
// jump to the next '' tag
$xmlReader->next(self::XML_NODE_SI);
@@ -128,19 +126,45 @@ public function extractSharedStrings()
$xmlReader->close();
}
+ /**
+ * Returns the shared string at the given index, using the previously chosen caching strategy.
+ *
+ * @param int $sharedStringIndex Index of the shared string in the sharedStrings.xml file
+ *
+ * @throws \OpenSpout\Reader\Exception\SharedStringNotFoundException If no shared string found for the given index
+ *
+ * @return string The shared string at the given index
+ */
+ public function getStringAtIndex($sharedStringIndex)
+ {
+ return $this->cachingStrategy->getStringAtIndex($sharedStringIndex);
+ }
+
+ /**
+ * Destroys the cache, freeing memory and removing any created artifacts.
+ */
+ public function cleanup()
+ {
+ if (null !== $this->cachingStrategy) {
+ $this->cachingStrategy->clearCache();
+ }
+ }
+
/**
* Returns the shared strings unique count, as specified in tag.
*
- * @param \Box\Spout\Reader\Wrapper\XMLReader $xmlReader XMLReader instance
- * @throws \Box\Spout\Common\Exception\IOException If sharedStrings.xml is invalid and can't be read
- * @return int|null Number of unique shared strings in the sharedStrings.xml file
+ * @param \OpenSpout\Reader\Wrapper\XMLReader $xmlReader XMLReader instance
+ *
+ * @throws \OpenSpout\Common\Exception\IOException If sharedStrings.xml is invalid and can't be read
+ *
+ * @return null|int Number of unique shared strings in the sharedStrings.xml file
*/
protected function getSharedStringsUniqueCount($xmlReader)
{
$xmlReader->next(self::XML_NODE_SST);
// Iterate over the "sst" elements to get the actual "sst ELEMENT" (skips any DOCTYPE)
- while ($xmlReader->getCurrentNodeName() === self::XML_NODE_SST && $xmlReader->nodeType !== XMLReader::ELEMENT) {
+ while (self::XML_NODE_SST === $xmlReader->getCurrentNodeName() && XMLReader::ELEMENT !== $xmlReader->nodeType) {
$xmlReader->read();
}
@@ -148,37 +172,39 @@ protected function getSharedStringsUniqueCount($xmlReader)
// some software do not add the "uniqueCount" attribute but only use the "count" one
// @see https://github.com/box/spout/issues/254
- if ($uniqueCount === null) {
+ if (null === $uniqueCount) {
$uniqueCount = $xmlReader->getAttribute(self::XML_ATTRIBUTE_COUNT);
}
- return ($uniqueCount !== null) ? (int) $uniqueCount : null;
+ return (null !== $uniqueCount) ? (int) $uniqueCount : null;
}
/**
* Returns the best shared strings caching strategy.
*
- * @param int|null $sharedStringsUniqueCount Number of unique shared strings (NULL if unknown)
+ * @param null|int $sharedStringsUniqueCount Number of unique shared strings (NULL if unknown)
+ *
* @return CachingStrategyInterface
*/
protected function getBestSharedStringsCachingStrategy($sharedStringsUniqueCount)
{
return $this->cachingStrategyFactory
- ->createBestCachingStrategy($sharedStringsUniqueCount, $this->tempFolder, $this->helperFactory);
+ ->createBestCachingStrategy($sharedStringsUniqueCount, $this->tempFolder, $this->helperFactory)
+ ;
}
/**
* Processes the shared strings item XML node which the given XML reader is positioned on.
*
- * @param \Box\Spout\Reader\Wrapper\XMLReader $xmlReader XML Reader positioned on a "" node
- * @param int $sharedStringIndex Index of the processed shared strings item
- * @return void
+ * @param \OpenSpout\Reader\Wrapper\XMLReader $xmlReader XML Reader positioned on a "" node
+ * @param int $sharedStringIndex Index of the processed shared strings item
*/
protected function processSharedStringsItem($xmlReader, $sharedStringIndex)
{
$sharedStringValue = '';
// NOTE: expand() will automatically decode all XML entities of the child nodes
+ /** @var \DOMElement $siNode */
$siNode = $xmlReader->expand();
$textNodes = $siNode->getElementsByTagName(self::XML_NODE_T);
@@ -187,7 +213,7 @@ protected function processSharedStringsItem($xmlReader, $sharedStringIndex)
$textNodeValue = $textNode->nodeValue;
$shouldPreserveWhitespace = $this->shouldPreserveWhitespace($textNode);
- $sharedStringValue .= ($shouldPreserveWhitespace) ? $textNodeValue : \trim($textNodeValue);
+ $sharedStringValue .= ($shouldPreserveWhitespace) ? $textNodeValue : trim($textNodeValue);
}
}
@@ -200,49 +226,27 @@ protected function processSharedStringsItem($xmlReader, $sharedStringIndex)
* We'll only consider the nodes whose parents are "" or "".
*
* @param \DOMElement $textNode Text node to check
+ *
* @return bool Whether the given text node's value must be extracted
*/
protected function shouldExtractTextNodeValue($textNode)
{
$parentTagName = $textNode->parentNode->localName;
- return ($parentTagName === self::XML_NODE_SI || $parentTagName === self::XML_NODE_R);
+ return self::XML_NODE_SI === $parentTagName || self::XML_NODE_R === $parentTagName;
}
/**
* If the text node has the attribute 'xml:space="preserve"', then preserve whitespace.
*
* @param \DOMElement $textNode The text node element () whose whitespace may be preserved
+ *
* @return bool Whether whitespace should be preserved
*/
protected function shouldPreserveWhitespace($textNode)
{
$spaceValue = $textNode->getAttribute(self::XML_ATTRIBUTE_XML_SPACE);
- return ($spaceValue === self::XML_ATTRIBUTE_VALUE_PRESERVE);
- }
-
- /**
- * Returns the shared string at the given index, using the previously chosen caching strategy.
- *
- * @param int $sharedStringIndex Index of the shared string in the sharedStrings.xml file
- * @throws \Box\Spout\Reader\Exception\SharedStringNotFoundException If no shared string found for the given index
- * @return string The shared string at the given index
- */
- public function getStringAtIndex($sharedStringIndex)
- {
- return $this->cachingStrategy->getStringAtIndex($sharedStringIndex);
- }
-
- /**
- * Destroys the cache, freeing memory and removing any created artifacts
- *
- * @return void
- */
- public function cleanup()
- {
- if ($this->cachingStrategy) {
- $this->cachingStrategy->clearCache();
- }
+ return self::XML_ATTRIBUTE_VALUE_PRESERVE === $spaceValue;
}
}
diff --git a/lib/spout/src/Spout/Reader/XLSX/Manager/SheetManager.php b/lib/openspout/src/Reader/XLSX/Manager/SheetManager.php
similarity index 71%
rename from lib/spout/src/Spout/Reader/XLSX/Manager/SheetManager.php
rename to lib/openspout/src/Reader/XLSX/Manager/SheetManager.php
index e7b41e0105fa5..e2e173e87b18c 100644
--- a/lib/spout/src/Spout/Reader/XLSX/Manager/SheetManager.php
+++ b/lib/openspout/src/Reader/XLSX/Manager/SheetManager.php
@@ -1,57 +1,56 @@
" starting node
+ * @param \OpenSpout\Reader\Wrapper\XMLReader $xmlReader XMLReader object, positioned on a "" starting node
+ *
* @return int A return code that indicates what action should the processor take next
*/
protected function processWorkbookPropertiesStartingNode($xmlReader)
{
// Using "filter_var($x, FILTER_VALIDATE_BOOLEAN)" here because the value of the "date1904" attribute
// may be the string "false", that is not mapped to the boolean "false" by default...
- $shouldUse1904Dates = \filter_var($xmlReader->getAttribute(self::XML_ATTRIBUTE_DATE_1904), FILTER_VALIDATE_BOOLEAN);
+ $shouldUse1904Dates = filter_var($xmlReader->getAttribute(self::XML_ATTRIBUTE_DATE_1904), FILTER_VALIDATE_BOOLEAN);
$this->optionsManager->setOption(Options::SHOULD_USE_1904_DATES, $shouldUse1904Dates);
return XMLProcessor::PROCESSING_CONTINUE;
}
/**
- * @param \Box\Spout\Reader\Wrapper\XMLReader $xmlReader XMLReader object, positioned on a "" starting node
+ * @param \OpenSpout\Reader\Wrapper\XMLReader $xmlReader XMLReader object, positioned on a "" starting node
+ *
* @return int A return code that indicates what action should the processor take next
*/
protected function processWorkbookViewStartingNode($xmlReader)
@@ -136,14 +137,15 @@ protected function processWorkbookViewStartingNode($xmlReader)
}
/**
- * @param \Box\Spout\Reader\Wrapper\XMLReader $xmlReader XMLReader object, positioned on a "" starting node
+ * @param \OpenSpout\Reader\Wrapper\XMLReader $xmlReader XMLReader object, positioned on a "" starting node
+ *
* @return int A return code that indicates what action should the processor take next
*/
protected function processSheetStartingNode($xmlReader)
{
$isSheetActive = ($this->currentSheetIndex === $this->activeSheetIndex);
$this->sheets[] = $this->getSheetFromSheetXMLNode($xmlReader, $this->currentSheetIndex, $isSheetActive);
- $this->currentSheetIndex++;
+ ++$this->currentSheetIndex;
return XMLProcessor::PROCESSING_CONTINUE;
}
@@ -161,17 +163,18 @@ protected function processSheetsEndingNode()
* We can find the XML file path describing the sheet inside "workbook.xml.res", by mapping with the sheet ID
* ("r:id" in "workbook.xml", "Id" in "workbook.xml.res").
*
- * @param \Box\Spout\Reader\Wrapper\XMLReader $xmlReaderOnSheetNode XML Reader instance, pointing on the node describing the sheet, as defined in "workbook.xml"
- * @param int $sheetIndexZeroBased Index of the sheet, based on order of appearance in the workbook (zero-based)
- * @param bool $isSheetActive Whether this sheet was defined as active
- * @return \Box\Spout\Reader\XLSX\Sheet Sheet instance
+ * @param \OpenSpout\Reader\Wrapper\XMLReader $xmlReaderOnSheetNode XML Reader instance, pointing on the node describing the sheet, as defined in "workbook.xml"
+ * @param int $sheetIndexZeroBased Index of the sheet, based on order of appearance in the workbook (zero-based)
+ * @param bool $isSheetActive Whether this sheet was defined as active
+ *
+ * @return \OpenSpout\Reader\XLSX\Sheet Sheet instance
*/
protected function getSheetFromSheetXMLNode($xmlReaderOnSheetNode, $sheetIndexZeroBased, $isSheetActive)
{
$sheetId = $xmlReaderOnSheetNode->getAttribute(self::XML_ATTRIBUTE_R_ID);
$sheetState = $xmlReaderOnSheetNode->getAttribute(self::XML_ATTRIBUTE_STATE);
- $isSheetVisible = ($sheetState !== self::SHEET_STATE_HIDDEN);
+ $isSheetVisible = (self::SHEET_STATE_HIDDEN !== $sheetState);
$escapedSheetName = $xmlReaderOnSheetNode->getAttribute(self::XML_ATTRIBUTE_NAME);
$sheetName = $this->escaper->unescape($escapedSheetName);
@@ -192,6 +195,7 @@ protected function getSheetFromSheetXMLNode($xmlReaderOnSheetNode, $sheetIndexZe
/**
* @param string $sheetId The sheet ID, as defined in "workbook.xml"
+ *
* @return string The XML file path describing the sheet inside "workbook.xml.res", for the given sheet ID
*/
protected function getSheetDataXMLFilePathForSheetId($sheetId)
@@ -211,8 +215,9 @@ protected function getSheetDataXMLFilePathForSheetId($sheetId)
$sheetDataXMLFilePath = $xmlReader->getAttribute(self::XML_ATTRIBUTE_TARGET);
// sometimes, the sheet data file path already contains "/xl/"...
- if (\strpos($sheetDataXMLFilePath, '/xl/') !== 0) {
- $sheetDataXMLFilePath = '/xl/' . $sheetDataXMLFilePath;
+ if (0 !== strpos($sheetDataXMLFilePath, '/xl/')) {
+ $sheetDataXMLFilePath = '/xl/'.$sheetDataXMLFilePath;
+
break;
}
}
diff --git a/lib/spout/src/Spout/Reader/XLSX/Manager/StyleManager.php b/lib/openspout/src/Reader/XLSX/Manager/StyleManager.php
similarity index 83%
rename from lib/spout/src/Spout/Reader/XLSX/Manager/StyleManager.php
rename to lib/openspout/src/Reader/XLSX/Manager/StyleManager.php
index d5db0d588cc9c..4eb90468bb01a 100644
--- a/lib/spout/src/Spout/Reader/XLSX/Manager/StyleManager.php
+++ b/lib/openspout/src/Reader/XLSX/Manager/StyleManager.php
@@ -1,33 +1,33 @@
FORMAT_CODE */
+ /** @var null|array Array containing a mapping NUM_FMT_ID => FORMAT_CODE */
protected $customNumberFormats;
- /** @var array Array containing a mapping STYLE_ID => [STYLE_ATTRIBUTES] */
+ /** @var null|array Array containing a mapping STYLE_ID => [STYLE_ATTRIBUTES] */
protected $stylesAttributes;
/** @var array Cache containing a mapping NUM_FMT_ID => IS_DATE_FORMAT. Used to avoid lots of recalculations */
protected $numFmtIdToIsDateFormatCache = [];
/**
- * @param string $filePath Path of the XLSX file being read
+ * @param string $filePath Path of the XLSX file being read
* @param WorkbookRelationshipsManager $workbookRelationshipsManager Helps retrieving workbook relationships
- * @param InternalEntityFactory $entityFactory Factory to create entities
+ * @param InternalEntityFactory $entityFactory Factory to create entities
*/
public function __construct($filePath, $workbookRelationshipsManager, $entityFactory)
{
$this->filePath = $filePath;
$this->entityFactory = $entityFactory;
- $this->builtinNumFmtIdIndicatingDates = \array_keys(self::$builtinNumFmtIdToNumFormatMapping);
+ $this->builtinNumFmtIdIndicatingDates = array_keys(self::$builtinNumFmtIdToNumFormatMapping);
$this->hasStylesXMLFile = $workbookRelationshipsManager->hasStylesXMLFile();
if ($this->hasStylesXMLFile) {
$this->stylesXMLFilePath = $workbookRelationshipsManager->getStylesXMLFilePath();
@@ -90,6 +90,7 @@ public function __construct($filePath, $workbookRelationshipsManager, $entityFac
* numeric values as timestamps and format the cell as a date.
*
* @param int $styleId Zero-based style ID
+ *
* @return bool Whether the cell with the given cell should display a date instead of a numeric value
*/
public function shouldFormatNumericValueAsDate($styleId)
@@ -103,7 +104,7 @@ public function shouldFormatNumericValueAsDate($styleId)
// Default style (0) does not format numeric values as timestamps. Only custom styles do.
// Also if the style ID does not exist in the styles.xml file, format as numeric value.
// Using isset here because it is way faster than array_key_exists...
- if ($styleId === self::DEFAULT_STYLE_ID || !isset($stylesAttributes[$styleId])) {
+ if (self::DEFAULT_STYLE_ID === $styleId || !isset($stylesAttributes[$styleId])) {
return false;
}
@@ -113,9 +114,31 @@ public function shouldFormatNumericValueAsDate($styleId)
}
/**
- * Reads the styles.xml file and extract the relevant information from the file.
+ * Returns the format as defined in "styles.xml" of the given style.
+ * NOTE: It is assumed that the style DOES have a number format associated to it.
+ *
+ * @param int $styleId Zero-based style ID
*
- * @return void
+ * @return string The number format code associated with the given style
+ */
+ public function getNumberFormatCode($styleId)
+ {
+ $stylesAttributes = $this->getStylesAttributes();
+ $styleAttributes = $stylesAttributes[$styleId];
+ $numFmtId = $styleAttributes[self::XML_ATTRIBUTE_NUM_FMT_ID];
+
+ if ($this->isNumFmtIdBuiltInDateFormat($numFmtId)) {
+ $numberFormatCode = self::$builtinNumFmtIdToNumFormatMapping[$numFmtId];
+ } else {
+ $customNumberFormats = $this->getCustomNumberFormats();
+ $numberFormatCode = $customNumberFormats[$numFmtId];
+ }
+
+ return $numberFormatCode;
+ }
+
+ /**
+ * Reads the styles.xml file and extract the relevant information from the file.
*/
protected function extractRelevantInfo()
{
@@ -142,8 +165,7 @@ protected function extractRelevantInfo()
* For simplicity, the styles attributes are kept in memory. This is possible thanks
* to the reuse of formats. So 1 million cells should not use 1 million formats.
*
- * @param \Box\Spout\Reader\Wrapper\XMLReader $xmlReader XML Reader positioned on the "numFmts" node
- * @return void
+ * @param \OpenSpout\Reader\Wrapper\XMLReader $xmlReader XML Reader positioned on the "numFmts" node
*/
protected function extractNumberFormats($xmlReader)
{
@@ -164,21 +186,20 @@ protected function extractNumberFormats($xmlReader)
* For simplicity, the styles attributes are kept in memory. This is possible thanks
* to the reuse of styles. So 1 million cells should not use 1 million styles.
*
- * @param \Box\Spout\Reader\Wrapper\XMLReader $xmlReader XML Reader positioned on the "cellXfs" node
- * @return void
+ * @param \OpenSpout\Reader\Wrapper\XMLReader $xmlReader XML Reader positioned on the "cellXfs" node
*/
protected function extractStyleAttributes($xmlReader)
{
while ($xmlReader->read()) {
if ($xmlReader->isPositionedOnStartingNode(self::XML_NODE_XF)) {
$numFmtId = $xmlReader->getAttribute(self::XML_ATTRIBUTE_NUM_FMT_ID);
- $normalizedNumFmtId = ($numFmtId !== null) ? (int) $numFmtId : null;
+ $normalizedNumFmtId = (null !== $numFmtId) ? (int) $numFmtId : null;
$applyNumberFormat = $xmlReader->getAttribute(self::XML_ATTRIBUTE_APPLY_NUMBER_FORMAT);
- $normalizedApplyNumberFormat = ($applyNumberFormat !== null) ? (bool) $applyNumberFormat : null;
+ $normalizedApplyNumberFormat = (null !== $applyNumberFormat) ? (bool) $applyNumberFormat : null;
$this->stylesAttributes[] = [
- self::XML_ATTRIBUTE_NUM_FMT_ID => $normalizedNumFmtId,
+ self::XML_ATTRIBUTE_NUM_FMT_ID => $normalizedNumFmtId,
self::XML_ATTRIBUTE_APPLY_NUMBER_FORMAT => $normalizedApplyNumberFormat,
];
} elseif ($xmlReader->isPositionedOnEndingNode(self::XML_NODE_CELL_XFS)) {
@@ -214,6 +235,7 @@ protected function getStylesAttributes()
/**
* @param array $styleAttributes Array containing the style attributes (2 keys: "applyNumberFormat" and "numFmtId")
+ *
* @return bool Whether the style with the given attributes indicates that the number is a date
*/
protected function doesStyleIndicateDate($styleAttributes)
@@ -226,7 +248,7 @@ protected function doesStyleIndicateDate($styleAttributes)
// - "numFmtId" attribute set
// This is a preliminary check, as having "numFmtId" set just means the style should apply a specific number format,
// but this is not necessarily a date.
- if ($applyNumberFormat === false || $numFmtId === null) {
+ if (false === $applyNumberFormat || null === $numFmtId) {
return false;
}
@@ -239,6 +261,7 @@ protected function doesStyleIndicateDate($styleAttributes)
* "numFmtId" attributes can be shared between multiple styles.
*
* @param int $numFmtId
+ *
* @return bool Whether the number format ID indicates that the number is a date
*/
protected function doesNumFmtIdIndicateDate($numFmtId)
@@ -247,8 +270,8 @@ protected function doesNumFmtIdIndicateDate($numFmtId)
$formatCode = $this->getFormatCodeForNumFmtId($numFmtId);
$this->numFmtIdToIsDateFormatCache[$numFmtId] = (
- $this->isNumFmtIdBuiltInDateFormat($numFmtId) ||
- $this->isFormatCodeCustomDateFormat($formatCode)
+ $this->isNumFmtIdBuiltInDateFormat($numFmtId)
+ || $this->isFormatCodeCustomDateFormat($formatCode)
);
}
@@ -257,7 +280,8 @@ protected function doesNumFmtIdIndicateDate($numFmtId)
/**
* @param int $numFmtId
- * @return string|null The custom number format or NULL if none defined for the given numFmtId
+ *
+ * @return null|string The custom number format or NULL if none defined for the given numFmtId
*/
protected function getFormatCodeForNumFmtId($numFmtId)
{
@@ -269,21 +293,23 @@ protected function getFormatCodeForNumFmtId($numFmtId)
/**
* @param int $numFmtId
+ *
* @return bool Whether the number format ID indicates that the number is a date
*/
protected function isNumFmtIdBuiltInDateFormat($numFmtId)
{
- return \in_array($numFmtId, $this->builtinNumFmtIdIndicatingDates);
+ return \in_array($numFmtId, $this->builtinNumFmtIdIndicatingDates, true);
}
/**
- * @param string|null $formatCode
+ * @param null|string $formatCode
+ *
* @return bool Whether the given format code indicates that the number is a date
*/
protected function isFormatCodeCustomDateFormat($formatCode)
{
// if no associated format code or if using the default "General" format
- if ($formatCode === null || \strcasecmp($formatCode, self::NUMBER_FORMAT_GENERAL) === 0) {
+ if (null === $formatCode || 0 === strcasecmp($formatCode, self::NUMBER_FORMAT_GENERAL)) {
return false;
}
@@ -292,13 +318,14 @@ protected function isFormatCodeCustomDateFormat($formatCode)
/**
* @param string $formatCode
+ *
* @return bool Whether the given format code matches a date format pattern
*/
protected function isFormatCodeMatchingDateFormatPattern($formatCode)
{
// Remove extra formatting (what's between [ ], the brackets should not be preceded by a "\")
$pattern = '((?getStylesAttributes();
- $styleAttributes = $stylesAttributes[$styleId];
- $numFmtId = $styleAttributes[self::XML_ATTRIBUTE_NUM_FMT_ID];
-
- if ($this->isNumFmtIdBuiltInDateFormat($numFmtId)) {
- $numberFormatCode = self::$builtinNumFmtIdToNumFormatMapping[$numFmtId];
- } else {
- $customNumberFormats = $this->getCustomNumberFormats();
- $numberFormatCode = $customNumberFormats[$numFmtId];
- }
-
- return $numberFormatCode;
- }
}
diff --git a/lib/spout/src/Spout/Reader/XLSX/Manager/WorkbookRelationshipsManager.php b/lib/openspout/src/Reader/XLSX/Manager/WorkbookRelationshipsManager.php
similarity index 69%
rename from lib/spout/src/Spout/Reader/XLSX/Manager/WorkbookRelationshipsManager.php
rename to lib/openspout/src/Reader/XLSX/Manager/WorkbookRelationshipsManager.php
index 0a9f6f51aa1b5..721ed72450175 100644
--- a/lib/spout/src/Spout/Reader/XLSX/Manager/WorkbookRelationshipsManager.php
+++ b/lib/openspout/src/Reader/XLSX/Manager/WorkbookRelationshipsManager.php
@@ -1,32 +1,31 @@
[FILE_NAME] */
+ /** @var null|array Cache of the already read workbook relationships: [TYPE] => [FILE_NAME] */
private $cachedWorkbookRelationships;
/**
- * @param string $filePath Path of the XLSX file being read
+ * @param string $filePath Path of the XLSX file being read
* @param InternalEntityFactory $entityFactory Factory to create entities
*/
public function __construct($filePath, $entityFactory)
@@ -57,10 +56,10 @@ public function getSharedStringsXMLFilePath()
?? $workbookRelationships[self::RELATIONSHIP_TYPE_SHARED_STRINGS_STRICT];
// the file path can be relative (e.g. "styles.xml") or absolute (e.g. "/xl/styles.xml")
- $doesContainBasePath = (\strpos($sharedStringsXMLFilePath, self::BASE_PATH) !== false);
+ $doesContainBasePath = (false !== strpos($sharedStringsXMLFilePath, self::BASE_PATH));
if (!$doesContainBasePath) {
// make sure we return an absolute file path
- $sharedStringsXMLFilePath = self::BASE_PATH . $sharedStringsXMLFilePath;
+ $sharedStringsXMLFilePath = self::BASE_PATH.$sharedStringsXMLFilePath;
}
return $sharedStringsXMLFilePath;
@@ -98,10 +97,10 @@ public function getStylesXMLFilePath()
?? $workbookRelationships[self::RELATIONSHIP_TYPE_STYLES_STRICT];
// the file path can be relative (e.g. "styles.xml") or absolute (e.g. "/xl/styles.xml")
- $doesContainBasePath = (\strpos($stylesXMLFilePath, self::BASE_PATH) !== false);
+ $doesContainBasePath = (false !== strpos($stylesXMLFilePath, self::BASE_PATH));
if (!$doesContainBasePath) {
// make sure we return a full path
- $stylesXMLFilePath = self::BASE_PATH . $stylesXMLFilePath;
+ $stylesXMLFilePath = self::BASE_PATH.$stylesXMLFilePath;
}
return $stylesXMLFilePath;
@@ -111,7 +110,8 @@ public function getStylesXMLFilePath()
* Reads the workbook.xml.rels and extracts the filename associated to the different types.
* It caches the result so that the file is read only once.
*
- * @throws \Box\Spout\Common\Exception\IOException If workbook.xml.rels can't be read
+ * @throws \OpenSpout\Common\Exception\IOException If workbook.xml.rels can't be read
+ *
* @return array
*/
private function getWorkbookRelationships()
@@ -119,8 +119,8 @@ private function getWorkbookRelationships()
if (!isset($this->cachedWorkbookRelationships)) {
$xmlReader = $this->entityFactory->createXMLReader();
- if ($xmlReader->openFileInZip($this->filePath, self::WORKBOOK_RELS_XML_FILE_PATH) === false) {
- throw new IOException('Could not open "' . self::WORKBOOK_RELS_XML_FILE_PATH . '".');
+ if (false === $xmlReader->openFileInZip($this->filePath, self::WORKBOOK_RELS_XML_FILE_PATH)) {
+ throw new IOException('Could not open "'.self::WORKBOOK_RELS_XML_FILE_PATH.'".');
}
$this->cachedWorkbookRelationships = [];
@@ -137,7 +137,6 @@ private function getWorkbookRelationships()
* Extracts and store the data of the current workbook relationship.
*
* @param XMLReader $xmlReader
- * @return void
*/
private function processWorkbookRelationship($xmlReader)
{
diff --git a/lib/spout/src/Spout/Reader/XLSX/Reader.php b/lib/openspout/src/Reader/XLSX/Reader.php
similarity index 67%
rename from lib/spout/src/Spout/Reader/XLSX/Reader.php
rename to lib/openspout/src/Reader/XLSX/Reader.php
index 689f6e2f155e4..197c7fc58d039 100644
--- a/lib/spout/src/Spout/Reader/XLSX/Reader.php
+++ b/lib/openspout/src/Reader/XLSX/Reader.php
@@ -1,19 +1,18 @@
zip = $entityFactory->createZipArchive();
- if ($this->zip->open($filePath) === true) {
+ if (true === $this->zip->open($filePath)) {
$tempFolder = $this->optionsManager->getOption(Options::TEMP_FOLDER);
$this->sharedStringsManager = $this->managerFactory->createSharedStringsManager($filePath, $tempFolder, $entityFactory);
@@ -98,7 +92,7 @@ protected function openReader($filePath)
$this->sharedStringsManager
);
} else {
- throw new IOException("Could not open $filePath for reading.");
+ throw new IOException("Could not open {$filePath} for reading.");
}
}
@@ -114,16 +108,14 @@ protected function getConcreteSheetIterator()
/**
* Closes the reader. To be used after reading the file.
- *
- * @return void
*/
protected function closeReader()
{
- if ($this->zip) {
+ if (null !== $this->zip) {
$this->zip->close();
}
- if ($this->sharedStringsManager) {
+ if (null !== $this->sharedStringsManager) {
$this->sharedStringsManager->cleanup();
}
}
diff --git a/lib/spout/src/Spout/Reader/XLSX/RowIterator.php b/lib/openspout/src/Reader/XLSX/RowIterator.php
similarity index 73%
rename from lib/spout/src/Spout/Reader/XLSX/RowIterator.php
rename to lib/openspout/src/Reader/XLSX/RowIterator.php
index 9c891a0415db7..de383706629b1 100644
--- a/lib/spout/src/Spout/Reader/XLSX/RowIterator.php
+++ b/lib/openspout/src/Reader/XLSX/RowIterator.php
@@ -1,36 +1,33 @@
xmlProcessor->registerCallback(self::XML_NODE_WORKSHEET, XMLProcessor::NODE_TYPE_END, [$this, 'processWorksheetEndingNode']);
}
- /**
- * @param string $sheetDataXMLFilePath Path of the sheet data XML file as in [Content_Types].xml
- * @return string Path of the XML file containing the sheet data,
- * without the leading slash.
- */
- protected function normalizeSheetDataXMLFilePath($sheetDataXMLFilePath)
- {
- return \ltrim($sheetDataXMLFilePath, '/');
- }
-
/**
* Rewind the Iterator to the first element.
* Initializes the XMLReader object that reads the associated sheet data.
* The XMLReader is configured to be safe from billion laughs attack.
+ *
* @see http://php.net/manual/en/iterator.rewind.php
*
- * @throws \Box\Spout\Common\Exception\IOException If the sheet data XML cannot be read
- * @return void
+ * @throws \OpenSpout\Common\Exception\IOException If the sheet data XML cannot be read
*/
#[\ReturnTypeWillChange]
- public function rewind()
+ public function rewind(): void
{
$this->xmlReader->close();
- if ($this->xmlReader->openFileInZip($this->filePath, $this->sheetDataXMLFilePath) === false) {
+ if (false === $this->xmlReader->openFileInZip($this->filePath, $this->sheetDataXMLFilePath)) {
throw new IOException("Could not open \"{$this->sheetDataXMLFilePath}\".");
}
@@ -159,35 +147,95 @@ public function rewind()
}
/**
- * Checks if current position is valid
- * @see http://php.net/manual/en/iterator.valid.php
+ * Checks if current position is valid.
*
- * @return bool
+ * @see http://php.net/manual/en/iterator.valid.php
*/
#[\ReturnTypeWillChange]
- public function valid()
+ public function valid(): bool
{
- return (!$this->hasReachedEndOfFile);
+ return !$this->hasReachedEndOfFile;
}
/**
* Move forward to next element. Reads data describing the next unprocessed row.
+ *
* @see http://php.net/manual/en/iterator.next.php
*
- * @throws \Box\Spout\Reader\Exception\SharedStringNotFoundException If a shared string was not found
- * @throws \Box\Spout\Common\Exception\IOException If unable to read the sheet data XML
- * @return void
+ * @throws \OpenSpout\Reader\Exception\SharedStringNotFoundException If a shared string was not found
+ * @throws \OpenSpout\Common\Exception\IOException If unable to read the sheet data XML
*/
#[\ReturnTypeWillChange]
- public function next()
+ public function next(): void
{
- $this->nextRowIndexToBeProcessed++;
+ ++$this->nextRowIndexToBeProcessed;
if ($this->doesNeedDataForNextRowToBeProcessed()) {
$this->readDataForNextRow();
}
}
+ /**
+ * Return the current element, either an empty row or from the buffer.
+ *
+ * @see http://php.net/manual/en/iterator.current.php
+ */
+ #[\ReturnTypeWillChange]
+ public function current(): ?Row
+ {
+ $rowToBeProcessed = $this->rowBuffer;
+
+ if ($this->shouldPreserveEmptyRows) {
+ // when we need to preserve empty rows, we will either return
+ // an empty row or the last row read. This depends whether the
+ // index of last row that was read matches the index of the last
+ // row whose value should be returned.
+ if ($this->lastRowIndexProcessed !== $this->nextRowIndexToBeProcessed) {
+ // return empty row if mismatch between last processed row
+ // and the row that needs to be returned
+ $rowToBeProcessed = $this->entityFactory->createRow();
+ }
+ }
+
+ return $rowToBeProcessed;
+ }
+
+ /**
+ * Return the key of the current element. Here, the row index.
+ *
+ * @see http://php.net/manual/en/iterator.key.php
+ */
+ #[\ReturnTypeWillChange]
+ public function key(): int
+ {
+ // TODO: This should return $this->nextRowIndexToBeProcessed
+ // but to avoid a breaking change, the return value for
+ // this function has been kept as the number of rows read.
+ return $this->shouldPreserveEmptyRows ?
+ $this->nextRowIndexToBeProcessed :
+ $this->numReadRows;
+ }
+
+ /**
+ * Cleans up what was created to iterate over the object.
+ */
+ #[\ReturnTypeWillChange]
+ public function end(): void
+ {
+ $this->xmlReader->close();
+ }
+
+ /**
+ * @param string $sheetDataXMLFilePath Path of the sheet data XML file as in [Content_Types].xml
+ *
+ * @return string path of the XML file containing the sheet data,
+ * without the leading slash
+ */
+ protected function normalizeSheetDataXMLFilePath($sheetDataXMLFilePath)
+ {
+ return ltrim($sheetDataXMLFilePath, '/');
+ }
+
/**
* Returns whether we need data for the next row to be processed.
* We don't need to read data if:
@@ -196,25 +244,24 @@ public function next()
* we need to preserve empty rows
* AND
* the last row that was read is not the row that need to be processed
- * (i.e. if we need to return empty rows)
+ * (i.e. if we need to return empty rows).
*
- * @return bool Whether we need data for the next row to be processed.
+ * @return bool whether we need data for the next row to be processed
*/
protected function doesNeedDataForNextRowToBeProcessed()
{
- $hasReadAtLeastOneRow = ($this->lastRowIndexProcessed !== 0);
+ $hasReadAtLeastOneRow = (0 !== $this->lastRowIndexProcessed);
- return (
- !$hasReadAtLeastOneRow ||
- !$this->shouldPreserveEmptyRows ||
- $this->lastRowIndexProcessed < $this->nextRowIndexToBeProcessed
- );
+ return
+ !$hasReadAtLeastOneRow
+ || !$this->shouldPreserveEmptyRows
+ || $this->lastRowIndexProcessed < $this->nextRowIndexToBeProcessed
+ ;
}
/**
- * @throws \Box\Spout\Reader\Exception\SharedStringNotFoundException If a shared string was not found
- * @throws \Box\Spout\Common\Exception\IOException If unable to read the sheet data XML
- * @return void
+ * @throws \OpenSpout\Reader\Exception\SharedStringNotFoundException If a shared string was not found
+ * @throws \OpenSpout\Common\Exception\IOException If unable to read the sheet data XML
*/
protected function readDataForNextRow()
{
@@ -230,14 +277,15 @@ protected function readDataForNextRow()
}
/**
- * @param \Box\Spout\Reader\Wrapper\XMLReader $xmlReader XMLReader object, positioned on a "" starting node
+ * @param \OpenSpout\Reader\Wrapper\XMLReader $xmlReader XMLReader object, positioned on a "" starting node
+ *
* @return int A return code that indicates what action should the processor take next
*/
protected function processDimensionStartingNode($xmlReader)
{
// Read dimensions of the sheet
$dimensionRef = $xmlReader->getAttribute(self::XML_ATTRIBUTE_REF); // returns 'A1:M13' for instance (or 'A1' for empty sheet)
- if (\preg_match('/[A-Z]+\d+:([A-Z]+\d+)/', $dimensionRef, $matches)) {
+ if (preg_match('/[A-Z]+\d+:([A-Z]+\d+)/', $dimensionRef, $matches)) {
$this->numColumns = CellHelper::getColumnIndexFromCellIndex($matches[1]) + 1;
}
@@ -245,7 +293,8 @@ protected function processDimensionStartingNode($xmlReader)
}
/**
- * @param \Box\Spout\Reader\Wrapper\XMLReader $xmlReader XMLReader object, positioned on a "" starting node
+ * @param \OpenSpout\Reader\Wrapper\XMLReader $xmlReader XMLReader object, positioned on a "" starting node
+ *
* @return int A return code that indicates what action should the processor take next
*/
protected function processRowStartingNode($xmlReader)
@@ -260,18 +309,19 @@ protected function processRowStartingNode($xmlReader)
$numberOfColumnsForRow = $this->numColumns;
$spans = $xmlReader->getAttribute(self::XML_ATTRIBUTE_SPANS); // returns '1:5' for instance
if ($spans) {
- list(, $numberOfColumnsForRow) = \explode(':', $spans);
+ [, $numberOfColumnsForRow] = explode(':', $spans);
$numberOfColumnsForRow = (int) $numberOfColumnsForRow;
}
- $cells = \array_fill(0, $numberOfColumnsForRow, $this->entityFactory->createCell(''));
+ $cells = array_fill(0, $numberOfColumnsForRow, $this->entityFactory->createCell(''));
$this->currentlyProcessedRow->setCells($cells);
return XMLProcessor::PROCESSING_CONTINUE;
}
/**
- * @param \Box\Spout\Reader\Wrapper\XMLReader $xmlReader XMLReader object, positioned on a "" starting node
+ * @param \OpenSpout\Reader\Wrapper\XMLReader $xmlReader XMLReader object, positioned on a "" starting node
+ *
* @return int A return code that indicates what action should the processor take next
*/
protected function processCellStartingNode($xmlReader)
@@ -279,6 +329,7 @@ protected function processCellStartingNode($xmlReader)
$currentColumnIndex = $this->getColumnIndex($xmlReader);
// NOTE: expand() will automatically decode all XML entities of the child nodes
+ /** @var \DOMElement $node */
$node = $xmlReader->expand();
$cell = $this->getCell($node);
@@ -299,10 +350,10 @@ protected function processRowEndingNode()
return XMLProcessor::PROCESSING_CONTINUE;
}
- $this->numReadRows++;
+ ++$this->numReadRows;
// If needed, we fill the empty cells
- if ($this->numColumns === 0) {
+ if (0 === $this->numColumns) {
$this->currentlyProcessedRow = $this->rowManager->fillMissingIndexesWithEmptyCells($this->currentlyProcessedRow);
}
@@ -323,8 +374,10 @@ protected function processWorksheetEndingNode()
}
/**
- * @param \Box\Spout\Reader\Wrapper\XMLReader $xmlReader XMLReader object, positioned on a "" node
- * @throws \Box\Spout\Common\Exception\InvalidArgumentException When the given cell index is invalid
+ * @param \OpenSpout\Reader\Wrapper\XMLReader $xmlReader XMLReader object, positioned on a "" node
+ *
+ * @throws \OpenSpout\Common\Exception\InvalidArgumentException When the given cell index is invalid
+ *
* @return int Row index
*/
protected function getRowIndex($xmlReader)
@@ -332,14 +385,16 @@ protected function getRowIndex($xmlReader)
// Get "r" attribute if present (from something like
$currentRowIndex = $xmlReader->getAttribute(self::XML_ATTRIBUTE_ROW_INDEX);
- return ($currentRowIndex !== null) ?
+ return (null !== $currentRowIndex) ?
(int) $currentRowIndex :
$this->lastRowIndexProcessed + 1;
}
/**
- * @param \Box\Spout\Reader\Wrapper\XMLReader $xmlReader XMLReader object, positioned on a "" node
- * @throws \Box\Spout\Common\Exception\InvalidArgumentException When the given cell index is invalid
+ * @param \OpenSpout\Reader\Wrapper\XMLReader $xmlReader XMLReader object, positioned on a "" node
+ *
+ * @throws \OpenSpout\Common\Exception\InvalidArgumentException When the given cell index is invalid
+ *
* @return int Column index
*/
protected function getColumnIndex($xmlReader)
@@ -347,7 +402,7 @@ protected function getColumnIndex($xmlReader)
// Get "r" attribute if present (from something like
$currentCellIndex = $xmlReader->getAttribute(self::XML_ATTRIBUTE_CELL_INDEX);
- return ($currentCellIndex !== null) ?
+ return (null !== $currentCellIndex) ?
CellHelper::getColumnIndexFromCellIndex($currentCellIndex) :
$this->lastColumnIndexProcessed + 1;
}
@@ -355,7 +410,8 @@ protected function getColumnIndex($xmlReader)
/**
* Returns the cell with (unescaped) correctly marshalled, cell value associated to the given XML node.
*
- * @param \DOMNode $node
+ * @param \DOMElement $node
+ *
* @return Cell The cell set with the associated with the cell
*/
protected function getCell($node)
@@ -370,58 +426,4 @@ protected function getCell($node)
return $cell;
}
-
- /**
- * Return the current element, either an empty row or from the buffer.
- * @see http://php.net/manual/en/iterator.current.php
- *
- * @return Row|null
- */
- #[\ReturnTypeWillChange]
- public function current()
- {
- $rowToBeProcessed = $this->rowBuffer;
-
- if ($this->shouldPreserveEmptyRows) {
- // when we need to preserve empty rows, we will either return
- // an empty row or the last row read. This depends whether the
- // index of last row that was read matches the index of the last
- // row whose value should be returned.
- if ($this->lastRowIndexProcessed !== $this->nextRowIndexToBeProcessed) {
- // return empty row if mismatch between last processed row
- // and the row that needs to be returned
- $rowToBeProcessed = $this->entityFactory->createRow();
- }
- }
-
- return $rowToBeProcessed;
- }
-
- /**
- * Return the key of the current element. Here, the row index.
- * @see http://php.net/manual/en/iterator.key.php
- *
- * @return int
- */
- #[\ReturnTypeWillChange]
- public function key()
- {
- // TODO: This should return $this->nextRowIndexToBeProcessed
- // but to avoid a breaking change, the return value for
- // this function has been kept as the number of rows read.
- return $this->shouldPreserveEmptyRows ?
- $this->nextRowIndexToBeProcessed :
- $this->numReadRows;
- }
-
- /**
- * Cleans up what was created to iterate over the object.
- *
- * @return void
- */
- #[\ReturnTypeWillChange]
- public function end()
- {
- $this->xmlReader->close();
- }
}
diff --git a/lib/spout/src/Spout/Reader/XLSX/Sheet.php b/lib/openspout/src/Reader/XLSX/Sheet.php
similarity index 70%
rename from lib/spout/src/Spout/Reader/XLSX/Sheet.php
rename to lib/openspout/src/Reader/XLSX/Sheet.php
index 575af2d076dd0..64d76374b3791 100644
--- a/lib/spout/src/Spout/Reader/XLSX/Sheet.php
+++ b/lib/openspout/src/Reader/XLSX/Sheet.php
@@ -1,16 +1,15 @@
sheets = $sheetManager->getSheets();
- if (\count($this->sheets) === 0) {
+ 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
+ * Rewind the Iterator to the first element.
*
- * @return void
+ * @see http://php.net/manual/en/iterator.rewind.php
*/
#[\ReturnTypeWillChange]
public function rewind()
@@ -45,7 +44,8 @@ public function rewind()
}
/**
- * Checks if current position is valid
+ * Checks if current position is valid.
+ *
* @see http://php.net/manual/en/iterator.valid.php
*
* @return bool
@@ -53,14 +53,13 @@ public function rewind()
#[\ReturnTypeWillChange]
public function valid()
{
- return ($this->currentSheetIndex < \count($this->sheets));
+ return $this->currentSheetIndex < \count($this->sheets);
}
/**
- * Move forward to next element
- * @see http://php.net/manual/en/iterator.next.php
+ * Move forward to next element.
*
- * @return void
+ * @see http://php.net/manual/en/iterator.next.php
*/
#[\ReturnTypeWillChange]
public function next()
@@ -70,15 +69,16 @@ public function next()
$currentSheet = $this->sheets[$this->currentSheetIndex];
$currentSheet->getRowIterator()->end();
- $this->currentSheetIndex++;
+ ++$this->currentSheetIndex;
}
}
/**
- * Return the current element
+ * Return the current element.
+ *
* @see http://php.net/manual/en/iterator.current.php
*
- * @return \Box\Spout\Reader\XLSX\Sheet
+ * @return \OpenSpout\Reader\XLSX\Sheet
*/
#[\ReturnTypeWillChange]
public function current()
@@ -87,7 +87,8 @@ public function current()
}
/**
- * Return the key of the current element
+ * Return the key of the current element.
+ *
* @see http://php.net/manual/en/iterator.key.php
*
* @return int
@@ -100,8 +101,6 @@ public function key()
/**
* Cleans up what was created to iterate over the object.
- *
- * @return void
*/
#[\ReturnTypeWillChange]
public function end()
diff --git a/lib/spout/src/Spout/Writer/CSV/Manager/OptionsManager.php b/lib/openspout/src/Writer/CSV/Manager/OptionsManager.php
similarity index 74%
rename from lib/spout/src/Spout/Writer/CSV/Manager/OptionsManager.php
rename to lib/openspout/src/Writer/CSV/Manager/OptionsManager.php
index 8acee695ee137..c6e25a6a07367 100644
--- a/lib/spout/src/Spout/Writer/CSV/Manager/OptionsManager.php
+++ b/lib/openspout/src/Writer/CSV/Manager/OptionsManager.php
@@ -1,13 +1,12 @@
optionsManager->getOption(Options::FIELD_ENCLOSURE);
$wasWriteSuccessful = $this->globalFunctionsHelper->fputcsv($this->filePointer, $row->getCells(), $fieldDelimiter, $fieldEnclosure);
- if ($wasWriteSuccessful === false) {
+ if (false === $wasWriteSuccessful) {
throw new IOException('Unable to write data');
}
- $this->lastWrittenRowIndex++;
- if ($this->lastWrittenRowIndex % self::FLUSH_THRESHOLD === 0) {
+ ++$this->lastWrittenRowIndex;
+ if (0 === $this->lastWrittenRowIndex % self::FLUSH_THRESHOLD) {
$this->globalFunctionsHelper->fflush($this->filePointer);
}
}
@@ -101,8 +101,6 @@ protected function addRowToWriter(Row $row)
/**
* Closes the CSV streamer, preventing any additional writing.
* If set, sets the headers and redirects output to the browser.
- *
- * @return void
*/
protected function closeWriter()
{
diff --git a/lib/spout/src/Spout/Writer/Common/Creator/InternalEntityFactory.php b/lib/openspout/src/Writer/Common/Creator/InternalEntityFactory.php
similarity index 56%
rename from lib/spout/src/Spout/Writer/Common/Creator/InternalEntityFactory.php
rename to lib/openspout/src/Writer/Common/Creator/InternalEntityFactory.php
index 5e8a1d4576983..63e96d3147f03 100644
--- a/lib/spout/src/Spout/Writer/Common/Creator/InternalEntityFactory.php
+++ b/lib/openspout/src/Writer/Common/Creator/InternalEntityFactory.php
@@ -1,15 +1,14 @@
style = new Style();
@@ -76,6 +72,7 @@ public function setFontStrikethrough()
* Sets the font size.
*
* @param int $fontSize Font size, in pixels
+ *
* @return StyleBuilder
*/
public function setFontSize($fontSize)
@@ -89,6 +86,7 @@ public function setFontSize($fontSize)
* Sets the font color.
*
* @param string $fontColor ARGB color (@see Color)
+ *
* @return StyleBuilder
*/
public function setFontColor($fontColor)
@@ -102,6 +100,7 @@ public function setFontColor($fontColor)
* Sets the font name.
*
* @param string $fontName Name of the font to use
+ *
* @return StyleBuilder
*/
public function setFontName($fontName)
@@ -112,9 +111,10 @@ public function setFontName($fontName)
}
/**
- * Makes the text wrap in the cell if requested
+ * Makes the text wrap in the cell if requested.
*
* @param bool $shouldWrap Should the text be wrapped
+ *
* @return StyleBuilder
*/
public function setShouldWrapText($shouldWrap = true)
@@ -130,6 +130,7 @@ public function setShouldWrapText($shouldWrap = true)
* @param string $cellAlignment The cell alignment
*
* @throws InvalidArgumentException If the given cell alignment is not valid
+ *
* @return StyleBuilder
*/
public function setCellAlignment($cellAlignment)
@@ -144,9 +145,8 @@ public function setCellAlignment($cellAlignment)
}
/**
- * Set a border
+ * Set a border.
*
- * @param Border $border
* @return $this
*/
public function setBorder(Border $border)
@@ -157,9 +157,10 @@ public function setBorder(Border $border)
}
/**
- * Sets a background color
+ * Sets a background color.
*
* @param string $color ARGB color (@see Color)
+ *
* @return StyleBuilder
*/
public function setBackgroundColor($color)
@@ -170,10 +171,12 @@ public function setBackgroundColor($color)
}
/**
- * Sets a format
+ * Sets a format.
*
* @param string $format Format
+ *
* @return StyleBuilder
+ *
* @api
*/
public function setFormat($format)
@@ -183,6 +186,22 @@ public function 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.
*
diff --git a/lib/spout/src/Spout/Writer/Common/Creator/WriterEntityFactory.php b/lib/openspout/src/Writer/Common/Creator/WriterEntityFactory.php
similarity index 65%
rename from lib/spout/src/Spout/Writer/Common/Creator/WriterEntityFactory.php
rename to lib/openspout/src/Writer/Common/Creator/WriterEntityFactory.php
index 8e43c31e3888b..da6a43ac2ef72 100644
--- a/lib/spout/src/Spout/Writer/Common/Creator/WriterEntityFactory.php
+++ b/lib/openspout/src/Writer/Common/Creator/WriterEntityFactory.php
@@ -1,25 +1,26 @@
sheetManager = $sheetManager;
$this->sheetManager->markWorkbookIdAsUsed($associatedWorkbookId);
- $this->setName(self::DEFAULT_SHEET_NAME_PREFIX . ($sheetIndex + 1));
+ $this->setName(self::DEFAULT_SHEET_NAME_PREFIX.($sheetIndex + 1));
$this->setIsVisible(true);
}
@@ -73,10 +76,12 @@ public function getName()
* - it should not be blank
* - it should not exceed 31 characters
* - it should not contain these characters: \ / ? * : [ or ]
- * - it should be unique
+ * - it should be unique.
*
* @param string $name Name of the sheet
- * @throws \Box\Spout\Writer\Exception\InvalidSheetNameException If the sheet's name is invalid.
+ *
+ * @throws \OpenSpout\Writer\Exception\InvalidSheetNameException if the sheet's name is invalid
+ *
* @return Sheet
*/
public function setName($name)
@@ -100,6 +105,7 @@ public function isVisible()
/**
* @param bool $isVisible Visibility of the sheet
+ *
* @return Sheet
*/
public function setIsVisible($isVisible)
@@ -108,4 +114,24 @@ public function setIsVisible($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;
+ }
}
diff --git a/lib/spout/src/Spout/Writer/Common/Entity/Workbook.php b/lib/openspout/src/Writer/Common/Entity/Workbook.php
similarity index 84%
rename from lib/spout/src/Spout/Writer/Common/Entity/Workbook.php
rename to lib/openspout/src/Writer/Common/Entity/Workbook.php
index dd182199da2c6..152ded82b8c81 100644
--- a/lib/spout/src/Spout/Writer/Common/Entity/Workbook.php
+++ b/lib/openspout/src/Writer/Common/Entity/Workbook.php
@@ -1,10 +1,9 @@
internalId = \uniqid();
+ $this->internalId = uniqid();
}
/**
diff --git a/lib/spout/src/Spout/Writer/Common/Entity/Worksheet.php b/lib/openspout/src/Writer/Common/Entity/Worksheet.php
similarity index 77%
rename from lib/spout/src/Spout/Writer/Common/Entity/Worksheet.php
rename to lib/openspout/src/Writer/Common/Entity/Worksheet.php
index 74c4976f074ca..0263429e74600 100644
--- a/lib/spout/src/Spout/Writer/Common/Entity/Worksheet.php
+++ b/lib/openspout/src/Writer/Common/Entity/Worksheet.php
@@ -1,17 +1,16 @@
externalSheet = $externalSheet;
$this->maxNumColumns = 0;
$this->lastWrittenRowIndex = 0;
+ $this->sheetDataStarted = false;
}
/**
@@ -110,4 +112,20 @@ 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;
+ }
}
diff --git a/lib/spout/src/Spout/Writer/Common/Helper/CellHelper.php b/lib/openspout/src/Writer/Common/Helper/CellHelper.php
similarity index 91%
rename from lib/spout/src/Spout/Writer/Common/Helper/CellHelper.php
rename to lib/openspout/src/Writer/Common/Helper/CellHelper.php
index afe3c712609b9..400e82ca2e312 100644
--- a/lib/spout/src/Spout/Writer/Common/Helper/CellHelper.php
+++ b/lib/openspout/src/Writer/Common/Helper/CellHelper.php
@@ -1,10 +1,9 @@
entityFactory->createZipArchive();
- $zipFilePath = $tmpFolderPath . self::ZIP_EXTENSION;
+ $zipFilePath = $tmpFolderPath.self::ZIP_EXTENSION;
$zip->open($zipFilePath, \ZipArchive::CREATE | \ZipArchive::OVERWRITE);
@@ -45,6 +45,7 @@ public function createZip($tmpFolderPath)
/**
* @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)
@@ -60,11 +61,10 @@ public function getZipFilePath(\ZipArchive $zip)
* 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
- * @return void
+ * @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)
{
@@ -85,11 +85,10 @@ public function addFileToArchive($zip, $rootFolderPath, $localFilePath, $existin
* 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
- * @return void
+ * @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)
{
@@ -102,56 +101,28 @@ public function addUncompressedFileToArchive($zip, $rootFolderPath, $localFilePa
);
}
- /**
- * 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
- * @return void
- */
- 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);
- }
- }
- }
-
/**
* @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'));
+ 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
- * @return void
+ * @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) . '/';
+ $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);
+ $itemLocalPath = str_replace($folderRealPath, '', $itemRealPath);
if ($itemInfo->isFile() && !$this->shouldSkipFile($zip, $itemLocalPath, $existingFileMode)) {
$zip->addFile($itemRealPath, $itemLocalPath);
@@ -159,10 +130,51 @@ public function addFolderToArchive($zip, $folderPath, $existingFileMode = self::
}
}
+ /**
+ * 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
+ * @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)
@@ -170,48 +182,33 @@ protected function shouldSkipFile($zip, $itemLocalPath, $existingFileMode)
// Skip files if:
// - EXISTING_FILES_SKIP mode chosen
// - File already exists in the archive
- return ($existingFileMode === self::EXISTING_FILES_SKIP && $zip->locateName($itemLocalPath) !== false);
+ 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);
+ $realPath = realpath($path);
- return \str_replace(DIRECTORY_SEPARATOR, '/', $realPath);
- }
-
- /**
- * 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
- * @return void
- */
- public function closeArchiveAndCopyToStream($zip, $streamPointer)
- {
- $zipFilePath = $zip->filename;
- $zip->close();
-
- $this->copyZipToStream($zipFilePath, $streamPointer);
+ return str_replace(\DIRECTORY_SEPARATOR, '/', $realPath);
}
/**
- * Streams the contents of the zip file into the given stream
+ * 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
- * @return void
+ * @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);
+ $zipFilePointer = fopen($zipFilePath, 'r');
+ stream_copy_to_stream($zipFilePointer, $pointer);
+ fclose($zipFilePointer);
}
}
diff --git a/lib/spout/src/Spout/Writer/Common/Manager/CellManager.php b/lib/openspout/src/Writer/Common/Manager/CellManager.php
similarity index 59%
rename from lib/spout/src/Spout/Writer/Common/Manager/CellManager.php
rename to lib/openspout/src/Writer/Common/Manager/CellManager.php
index 248aed4f39238..5ce70d4b2b418 100644
--- a/lib/spout/src/Spout/Writer/Common/Manager/CellManager.php
+++ b/lib/openspout/src/Writer/Common/Manager/CellManager.php
@@ -1,10 +1,10 @@
styleMerger = $styleMerger;
@@ -23,10 +20,6 @@ public function __construct(StyleMerger $styleMerger)
/**
* Merges a Style into a cell's Style.
- *
- * @param Cell $cell
- * @param Style $style
- * @return void
*/
public function applyStyle(Cell $cell, Style $style)
{
diff --git a/lib/openspout/src/Writer/Common/Manager/ManagesCellSize.php b/lib/openspout/src/Writer/Common/Manager/ManagesCellSize.php
new file mode 100644
index 0000000000000..eb8c4a4f0ba83
--- /dev/null
+++ b/lib/openspout/src/Writer/Common/Manager/ManagesCellSize.php
@@ -0,0 +1,62 @@
+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];
+ }
+}
diff --git a/lib/spout/src/Spout/Writer/Common/Manager/RegisteredStyle.php b/lib/openspout/src/Writer/Common/Manager/RegisteredStyle.php
similarity index 71%
rename from lib/spout/src/Spout/Writer/Common/Manager/RegisteredStyle.php
rename to lib/openspout/src/Writer/Common/Manager/RegisteredStyle.php
index 734c2b61da524..d3ef877ae8d45 100644
--- a/lib/spout/src/Spout/Writer/Common/Manager/RegisteredStyle.php
+++ b/lib/openspout/src/Writer/Common/Manager/RegisteredStyle.php
@@ -1,11 +1,10 @@
isMatchingRowStyle = $isMatchingRowStyle;
}
- public function getStyle() : Style
+ public function getStyle(): Style
{
return $this->style;
}
- public function isMatchingRowStyle() : bool
+ public function isMatchingRowStyle(): bool
{
return $this->isMatchingRowStyle;
}
diff --git a/lib/spout/src/Spout/Writer/Common/Manager/RowManager.php b/lib/openspout/src/Writer/Common/Manager/RowManager.php
similarity index 79%
rename from lib/spout/src/Spout/Writer/Common/Manager/RowManager.php
rename to lib/openspout/src/Writer/Common/Manager/RowManager.php
index 75b4b3a7451db..e702bf7af61ad 100644
--- a/lib/spout/src/Spout/Writer/Common/Manager/RowManager.php
+++ b/lib/openspout/src/Writer/Common/Manager/RowManager.php
@@ -1,8 +1,8 @@
isNameUnique($name, $sheet)) {
$failedRequirements[] = 'It should be unique';
} else {
- if ($nameLength === 0) {
+ if (0 === $nameLength) {
$failedRequirements[] = 'It should not be blank';
} else {
if ($nameLength > self::MAX_LENGTH_SHEET_NAME) {
@@ -74,45 +73,65 @@ public function throwIfNameIsInvalid($name, Sheet $sheet)
}
}
- if (\count($failedRequirements) !== 0) {
- $errorMessage = "The sheet's name (\"$name\") is invalid. It did not respect these rules:\n - ";
- $errorMessage .= \implode("\n - ", $failedRequirements);
+ if (0 !== \count($failedRequirements)) {
+ $errorMessage = "The sheet's name (\"{$name}\") is invalid. It did not respect these rules:\n - ";
+ $errorMessage .= implode("\n - ", $failedRequirements);
+
throw new InvalidSheetNameException($errorMessage);
}
}
+ /**
+ * @param string $workbookId Workbook ID associated to a Sheet
+ */
+ public function markWorkbookIdAsUsed($workbookId)
+ {
+ if (!isset(self::$SHEETS_NAME_USED[$workbookId])) {
+ self::$SHEETS_NAME_USED[$workbookId] = [];
+ }
+ }
+
+ public function markSheetNameAsUsed(Sheet $sheet)
+ {
+ self::$SHEETS_NAME_USED[$sheet->getAssociatedWorkbookId()][$sheet->getIndex()] = $sheet->getName();
+ }
+
/**
* Returns whether the given name contains at least one invalid character.
+ *
* @see Sheet::$INVALID_CHARACTERS_IN_SHEET_NAME for the full list.
*
* @param string $name
- * @return bool TRUE if the name contains invalid characters, FALSE otherwise.
+ *
+ * @return bool TRUE if the name contains invalid characters, FALSE otherwise
*/
private function doesContainInvalidCharacters($name)
{
- return (\str_replace(self::$INVALID_CHARACTERS_IN_SHEET_NAME, '', $name) !== $name);
+ return str_replace(self::$INVALID_CHARACTERS_IN_SHEET_NAME, '', $name) !== $name;
}
/**
- * Returns whether the given name starts or ends with a single quote
+ * Returns whether the given name starts or ends with a single quote.
*
* @param string $name
- * @return bool TRUE if the name starts or ends with a single quote, FALSE otherwise.
+ *
+ * @return bool TRUE if the name starts or ends with a single quote, FALSE otherwise
*/
private function doesStartOrEndWithSingleQuote($name)
{
- $startsWithSingleQuote = ($this->stringHelper->getCharFirstOccurrencePosition('\'', $name) === 0);
+ $startsWithSingleQuote = (0 === $this->stringHelper->getCharFirstOccurrencePosition('\'', $name));
$endsWithSingleQuote = ($this->stringHelper->getCharLastOccurrencePosition('\'', $name) === ($this->stringHelper->getStringLength($name) - 1));
- return ($startsWithSingleQuote || $endsWithSingleQuote);
+ return $startsWithSingleQuote || $endsWithSingleQuote;
}
/**
* Returns whether the given name is unique.
*
* @param string $name
- * @param Sheet $sheet The sheet whose future name is checked
- * @return bool TRUE if the name is unique, FALSE otherwise.
+ * @param Sheet $sheet The sheet whose future name is checked
+ *
+ * @return bool TRUE if the name is unique, FALSE otherwise
*/
private function isNameUnique($name, Sheet $sheet)
{
@@ -124,24 +143,4 @@ private function isNameUnique($name, Sheet $sheet)
return true;
}
-
- /**
- * @param int $workbookId Workbook ID associated to a Sheet
- * @return void
- */
- public function markWorkbookIdAsUsed($workbookId)
- {
- if (!isset(self::$SHEETS_NAME_USED[$workbookId])) {
- self::$SHEETS_NAME_USED[$workbookId] = [];
- }
- }
-
- /**
- * @param Sheet $sheet
- * @return void
- */
- public function markSheetNameAsUsed(Sheet $sheet)
- {
- self::$SHEETS_NAME_USED[$sheet->getAssociatedWorkbookId()][$sheet->getIndex()] = $sheet->getName();
- }
}
diff --git a/lib/spout/src/Spout/Writer/Common/Manager/Style/PossiblyUpdatedStyle.php b/lib/openspout/src/Writer/Common/Manager/Style/PossiblyUpdatedStyle.php
similarity index 67%
rename from lib/spout/src/Spout/Writer/Common/Manager/Style/PossiblyUpdatedStyle.php
rename to lib/openspout/src/Writer/Common/Manager/Style/PossiblyUpdatedStyle.php
index 6ccaa29d953e4..d78dd4b0b0fbd 100644
--- a/lib/spout/src/Spout/Writer/Common/Manager/Style/PossiblyUpdatedStyle.php
+++ b/lib/openspout/src/Writer/Common/Manager/Style/PossiblyUpdatedStyle.php
@@ -1,11 +1,10 @@
isUpdated = $isUpdated;
}
- public function getStyle() : Style
+ public function getStyle(): Style
{
return $this->style;
}
- public function isUpdated() : bool
+ public function isUpdated(): bool
{
return $this->isUpdated;
}
diff --git a/lib/spout/src/Spout/Writer/Common/Manager/Style/StyleManager.php b/lib/openspout/src/Writer/Common/Manager/Style/StyleManager.php
similarity index 80%
rename from lib/spout/src/Spout/Writer/Common/Manager/Style/StyleManager.php
rename to lib/openspout/src/Writer/Common/Manager/Style/StyleManager.php
index e2b5ebdb107f1..47f4d34cf3f26 100644
--- a/lib/spout/src/Spout/Writer/Common/Manager/Style/StyleManager.php
+++ b/lib/openspout/src/Writer/Common/Manager/Style/StyleManager.php
@@ -1,44 +1,30 @@
styleRegistry = $styleRegistry;
}
- /**
- * Returns the default style
- *
- * @return Style Default style
- */
- protected function getDefaultStyle()
- {
- // By construction, the default style has ID 0
- return $this->styleRegistry->getRegisteredStyles()[0];
- }
-
/**
* Registers the given style as a used style.
* Duplicate styles won't be registered more than once.
*
* @param Style $style The style to be registered
- * @return Style The registered style, updated with an internal ID.
+ *
+ * @return Style the registered style, updated with an internal ID
*/
public function registerStyle($style)
{
@@ -49,14 +35,24 @@ public function registerStyle($style)
* Apply additional styles if the given row needs it.
* Typically, set "wrap text" if a cell contains a new line.
*
- * @param Cell $cell
* @return PossiblyUpdatedStyle The eventually updated style
*/
- public function applyExtraStylesIfNeeded(Cell $cell) : PossiblyUpdatedStyle
+ public function applyExtraStylesIfNeeded(Cell $cell): PossiblyUpdatedStyle
{
return $this->applyWrapTextIfCellContainsNewLine($cell);
}
+ /**
+ * Returns the default style.
+ *
+ * @return Style Default style
+ */
+ protected function getDefaultStyle()
+ {
+ // By construction, the default style has ID 0
+ return $this->styleRegistry->getRegisteredStyles()[0];
+ }
+
/**
* Set the "wrap text" option if a cell of the given row contains a new line.
*
@@ -67,14 +63,15 @@ public function applyExtraStylesIfNeeded(Cell $cell) : PossiblyUpdatedStyle
* on the Windows version of Excel...
*
* @param Cell $cell The cell the style should be applied to
+ *
* @return PossiblyUpdatedStyle The eventually updated style
*/
- protected function applyWrapTextIfCellContainsNewLine(Cell $cell) : PossiblyUpdatedStyle
+ protected function applyWrapTextIfCellContainsNewLine(Cell $cell): PossiblyUpdatedStyle
{
$cellStyle = $cell->getStyle();
// if the "wrap text" option is already set, no-op
- if (!$cellStyle->hasSetWrapText() && $cell->isString() && \strpos($cell->getValue(), "\n") !== false) {
+ if (!$cellStyle->hasSetWrapText() && $cell->isString() && false !== strpos($cell->getValue(), "\n")) {
$cellStyle->setShouldWrapText();
return new PossiblyUpdatedStyle($cellStyle, true);
diff --git a/lib/spout/src/Spout/Writer/Common/Manager/Style/StyleManagerInterface.php b/lib/openspout/src/Writer/Common/Manager/Style/StyleManagerInterface.php
similarity index 59%
rename from lib/spout/src/Spout/Writer/Common/Manager/Style/StyleManagerInterface.php
rename to lib/openspout/src/Writer/Common/Manager/Style/StyleManagerInterface.php
index 6b320b1d5b970..0418f3dac331f 100644
--- a/lib/spout/src/Spout/Writer/Common/Manager/Style/StyleManagerInterface.php
+++ b/lib/openspout/src/Writer/Common/Manager/Style/StyleManagerInterface.php
@@ -1,12 +1,12 @@
hasSetFontSize() && $baseStyle->getFontSize() !== Style::DEFAULT_FONT_SIZE) {
+ if (!$style->hasSetFontSize() && Style::DEFAULT_FONT_SIZE !== $baseStyle->getFontSize()) {
$styleToUpdate->setFontSize($baseStyle->getFontSize());
}
- if (!$style->hasSetFontColor() && $baseStyle->getFontColor() !== Style::DEFAULT_FONT_COLOR) {
+ if (!$style->hasSetFontColor() && Style::DEFAULT_FONT_COLOR !== $baseStyle->getFontColor()) {
$styleToUpdate->setFontColor($baseStyle->getFontColor());
}
- if (!$style->hasSetFontName() && $baseStyle->getFontName() !== Style::DEFAULT_FONT_NAME) {
+ if (!$style->hasSetFontName() && Style::DEFAULT_FONT_NAME !== $baseStyle->getFontName()) {
$styleToUpdate->setFontName($baseStyle->getFontName());
}
}
/**
* @param Style $styleToUpdate Style to update (passed as reference)
- * @param Style $style
- * @param Style $baseStyle
- * @return void
*/
private function mergeCellProperties(Style $styleToUpdate, Style $style, Style $baseStyle)
{
if (!$style->hasSetWrapText() && $baseStyle->shouldWrapText()) {
$styleToUpdate->setShouldWrapText();
}
+ if (!$style->hasSetShrinkToFit() && $baseStyle->shouldShrinkToFit()) {
+ $styleToUpdate->setShouldShrinkToFit();
+ }
if (!$style->hasSetCellAlignment() && $baseStyle->shouldApplyCellAlignment()) {
$styleToUpdate->setCellAlignment($baseStyle->getCellAlignment());
}
- if (!$style->getBorder() && $baseStyle->shouldApplyBorder()) {
+ if (null === $style->getBorder() && $baseStyle->shouldApplyBorder()) {
$styleToUpdate->setBorder($baseStyle->getBorder());
}
- if (!$style->getFormat() && $baseStyle->shouldApplyFormat()) {
+ if (null === $style->getFormat() && $baseStyle->shouldApplyFormat()) {
$styleToUpdate->setFormat($baseStyle->getFormat());
}
if (!$style->shouldApplyBackgroundColor() && $baseStyle->shouldApplyBackgroundColor()) {
diff --git a/lib/spout/src/Spout/Writer/Common/Manager/Style/StyleRegistry.php b/lib/openspout/src/Writer/Common/Manager/Style/StyleRegistry.php
similarity index 88%
rename from lib/spout/src/Spout/Writer/Common/Manager/Style/StyleRegistry.php
rename to lib/openspout/src/Writer/Common/Manager/Style/StyleRegistry.php
index 6b439a75d6437..b3782a02cc1f3 100644
--- a/lib/spout/src/Spout/Writer/Common/Manager/Style/StyleRegistry.php
+++ b/lib/openspout/src/Writer/Common/Manager/Style/StyleRegistry.php
@@ -1,12 +1,11 @@
[STYLE] mapping table, keeping track of the registered styles */
protected $styleIdToStyleMappingTable = [];
- /**
- * @param Style $defaultStyle
- */
public function __construct(Style $defaultStyle)
{
// This ensures that the default style is the first one to be registered
@@ -30,7 +26,8 @@ public function __construct(Style $defaultStyle)
* Duplicate styles won't be registered more than once.
*
* @param Style $style The style to be registered
- * @return Style The registered style, updated with an internal ID.
+ *
+ * @return Style the registered style, updated with an internal ID
*/
public function registerStyle(Style $style)
{
@@ -47,41 +44,17 @@ public function registerStyle(Style $style)
return $this->getStyleFromSerializedStyle($serializedStyle);
}
- /**
- * Returns whether the serialized style has already been registered.
- *
- * @param string $serializedStyle The serialized style
- * @return bool
- */
- protected function hasSerializedStyleAlreadyBeenRegistered(string $serializedStyle)
- {
- // Using isset here because it is way faster than array_key_exists...
- return isset($this->serializedStyleToStyleIdMappingTable[$serializedStyle]);
- }
-
- /**
- * Returns the registered style associated to the given serialization.
- *
- * @param string $serializedStyle The serialized style from which the actual style should be fetched from
- * @return Style
- */
- protected function getStyleFromSerializedStyle($serializedStyle)
- {
- $styleId = $this->serializedStyleToStyleIdMappingTable[$serializedStyle];
-
- return $this->styleIdToStyleMappingTable[$styleId];
- }
-
/**
* @return Style[] List of registered styles
*/
public function getRegisteredStyles()
{
- return \array_values($this->styleIdToStyleMappingTable);
+ return array_values($this->styleIdToStyleMappingTable);
}
/**
* @param int $styleId
+ *
* @return Style
*/
public function getStyleFromStyleId($styleId)
@@ -94,7 +67,6 @@ public function getStyleFromStyleId($styleId)
* The ID is excluded from the comparison, as we only care about
* actual style properties.
*
- * @param Style $style
* @return string The serialized style
*/
public function serialize(Style $style)
@@ -103,10 +75,37 @@ public function serialize(Style $style)
$currentId = $style->getId();
$style->unmarkAsRegistered();
- $serializedStyle = \serialize($style);
+ $serializedStyle = serialize($style);
$style->markAsRegistered($currentId);
return $serializedStyle;
}
+
+ /**
+ * Returns whether the serialized style has already been registered.
+ *
+ * @param string $serializedStyle The serialized style
+ *
+ * @return bool
+ */
+ protected function hasSerializedStyleAlreadyBeenRegistered(string $serializedStyle)
+ {
+ // Using isset here because it is way faster than array_key_exists...
+ return isset($this->serializedStyleToStyleIdMappingTable[$serializedStyle]);
+ }
+
+ /**
+ * Returns the registered style associated to the given serialization.
+ *
+ * @param string $serializedStyle The serialized style from which the actual style should be fetched from
+ *
+ * @return Style
+ */
+ protected function getStyleFromSerializedStyle($serializedStyle)
+ {
+ $styleId = $this->serializedStyleToStyleIdMappingTable[$serializedStyle];
+
+ return $this->styleIdToStyleMappingTable[$styleId];
+ }
}
diff --git a/lib/spout/src/Spout/Writer/Common/Manager/WorkbookManagerAbstract.php b/lib/openspout/src/Writer/Common/Manager/WorkbookManagerAbstract.php
similarity index 74%
rename from lib/spout/src/Spout/Writer/Common/Manager/WorkbookManagerAbstract.php
rename to lib/openspout/src/Writer/Common/Manager/WorkbookManagerAbstract.php
index 653778c7047c9..8ffd7e91c67a7 100644
--- a/lib/spout/src/Spout/Writer/Common/Manager/WorkbookManagerAbstract.php
+++ b/lib/openspout/src/Writer/Common/Manager/WorkbookManagerAbstract.php
@@ -1,29 +1,27 @@
getWorksheets();
-
- $newSheetIndex = \count($worksheets);
- $sheetManager = $this->managerFactory->createSheetManager();
- $sheet = $this->entityFactory->createSheet($newSheetIndex, $this->workbook->getInternalId(), $sheetManager);
-
- $worksheetFilePath = $this->getWorksheetFilePath($sheet);
- $worksheet = $this->entityFactory->createWorksheet($worksheetFilePath, $sheet);
-
- $this->worksheetManager->startSheet($worksheet);
-
- $worksheets[] = $worksheet;
- $this->workbook->setWorksheets($worksheets);
-
- return $worksheet;
- }
-
/**
* @return Worksheet[] All the workbook's sheets
*/
@@ -148,7 +99,7 @@ public function getWorksheets()
}
/**
- * Returns the current sheet
+ * Returns the current sheet.
*
* @return Worksheet The current sheet
*/
@@ -157,62 +108,43 @@ public function getCurrentWorksheet()
return $this->currentWorksheet;
}
+ /**
+ * Starts the current sheet and opens the file pointer.
+ *
+ * @throws IOException
+ */
+ public function startCurrentSheet()
+ {
+ $this->worksheetManager->startSheet($this->getCurrentWorksheet());
+ }
+
/**
* 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 "external" sheet to set as current
+ *
* @throws SheetNotFoundException If the given sheet does not exist in the workbook
- * @return void
*/
public function setCurrentSheet(Sheet $sheet)
{
$worksheet = $this->getWorksheetFromExternalSheet($sheet);
- if ($worksheet !== null) {
+ if (null !== $worksheet) {
$this->currentWorksheet = $worksheet;
} else {
throw new SheetNotFoundException('The given sheet does not exist in the workbook.');
}
}
- /**
- * @param Worksheet $worksheet
- * @return void
- */
- private function setCurrentWorksheet($worksheet)
- {
- $this->currentWorksheet = $worksheet;
- }
-
- /**
- * Returns the worksheet associated to the given external sheet.
- *
- * @param Sheet $sheet
- * @return Worksheet|null The worksheet associated to the given external sheet or null if not found.
- */
- private function getWorksheetFromExternalSheet($sheet)
- {
- $worksheetFound = null;
-
- foreach ($this->getWorksheets() as $worksheet) {
- if ($worksheet->getExternalSheet() === $sheet) {
- $worksheetFound = $worksheet;
- break;
- }
- }
-
- return $worksheetFound;
- }
-
/**
* Adds a row to the current sheet.
* If shouldCreateNewSheetsAutomatically option is set to true, it will handle pagination
* with the creation of new worksheets if one worksheet has reached its maximum capicity.
*
* @param Row $row The row to be added
- * @throws IOException If trying to create a new sheet and unable to open the sheet for writing
- * @throws WriterException If unable to write data
- * @return void
+ *
+ * @throws IOException If trying to create a new sheet and unable to open the sheet for writing
+ * @throws \OpenSpout\Common\Exception\InvalidArgumentException
*/
public function addRowToCurrentWorksheet(Row $row)
{
@@ -226,54 +158,39 @@ public function addRowToCurrentWorksheet(Row $row)
$currentWorksheet = $this->addNewSheetAndMakeItCurrent();
$this->addRowToWorksheet($currentWorksheet, $row);
- } else {
- // otherwise, do nothing as the data won't be written anyways
}
+ // otherwise, do nothing as the data won't be written anyways
} else {
$this->addRowToWorksheet($currentWorksheet, $row);
}
}
- /**
- * @return bool Whether the current worksheet has reached the maximum number of rows per sheet.
- */
- private function hasCurrentWorksheetReachedMaxRows()
+ public function setDefaultColumnWidth(float $width)
{
- $currentWorksheet = $this->getCurrentWorksheet();
+ $this->worksheetManager->setDefaultColumnWidth($width);
+ }
- return ($currentWorksheet->getLastWrittenRowIndex() >= $this->getMaxRowsPerWorksheet());
+ public function setDefaultRowHeight(float $height)
+ {
+ $this->worksheetManager->setDefaultRowHeight($height);
}
/**
- * Adds a row to the given sheet.
- *
- * @param Worksheet $worksheet Worksheet to write the row to
- * @param Row $row The row to be added
- * @throws WriterException If unable to write data
- * @return void
+ * @param int ...$columns One or more columns with this width
*/
- private function addRowToWorksheet(Worksheet $worksheet, Row $row)
+ public function setColumnWidth(float $width, ...$columns)
{
- $this->applyDefaultRowStyle($row);
- $this->worksheetManager->addRow($worksheet, $row);
-
- // update max num columns for the worksheet
- $currentMaxNumColumns = $worksheet->getMaxNumColumns();
- $cellsCount = $row->getNumCells();
- $worksheet->setMaxNumColumns(\max($currentMaxNumColumns, $cellsCount));
+ $this->worksheetManager->setColumnWidth($width, ...$columns);
}
/**
- * @param Row $row
+ * @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
*/
- private function applyDefaultRowStyle(Row $row)
+ public function setColumnWidthForRange(float $width, int $start, int $end)
{
- $defaultRowStyle = $this->optionsManager->getOption(Options::DEFAULT_ROW_STYLE);
-
- if ($defaultRowStyle !== null) {
- $mergedStyle = $this->styleMerger->merge($row->getStyle(), $defaultRowStyle);
- $row->setStyle($mergedStyle);
- }
+ $this->worksheetManager->setColumnWidthForRange($width, $start, $end);
}
/**
@@ -282,7 +199,6 @@ private function applyDefaultRowStyle(Row $row)
* All the temporary files are then deleted.
*
* @param resource $finalFilePointer Pointer to the spreadsheet that will be created
- * @return void
*/
public function close($finalFilePointer)
{
@@ -293,9 +209,17 @@ public function close($finalFilePointer)
}
/**
- * Closes custom objects that are still opened
- *
- * @return void
+ * @return int Maximum number of rows/columns a sheet can contain
+ */
+ abstract protected function getMaxRowsPerWorksheet();
+
+ /**
+ * @return string The file path where the data for the given sheet will be stored
+ */
+ abstract protected function getWorksheetFilePath(Sheet $sheet);
+
+ /**
+ * Closes custom objects that are still opened.
*/
protected function closeRemainingObjects()
{
@@ -306,32 +230,123 @@ protected function closeRemainingObjects()
* 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
- * @return void
*/
abstract protected function writeAllFilesToDiskAndZipThem($finalFilePointer);
/**
- * Closes all workbook's associated sheets.
+ * Deletes the root folder created in the temp folder and all its contents.
+ */
+ protected function cleanupTempFolder()
+ {
+ $rootFolder = $this->fileSystemHelper->getRootFolder();
+ $this->fileSystemHelper->deleteFolderRecursively($rootFolder);
+ }
+
+ /**
+ * Creates a new sheet in the workbook. The current sheet remains unchanged.
*
- * @return void
+ * @throws \OpenSpout\Common\Exception\IOException If unable to open the sheet for writing
+ *
+ * @return Worksheet The created sheet
*/
- private function closeAllWorksheets()
+ private function addNewSheet()
{
$worksheets = $this->getWorksheets();
- foreach ($worksheets as $worksheet) {
- $this->worksheetManager->close($worksheet);
+ $newSheetIndex = \count($worksheets);
+ $sheetManager = $this->managerFactory->createSheetManager();
+ $sheet = $this->entityFactory->createSheet($newSheetIndex, $this->workbook->getInternalId(), $sheetManager);
+
+ $worksheetFilePath = $this->getWorksheetFilePath($sheet);
+ $worksheet = $this->entityFactory->createWorksheet($worksheetFilePath, $sheet);
+
+ $this->worksheetManager->startSheet($worksheet);
+
+ $worksheets[] = $worksheet;
+ $this->workbook->setWorksheets($worksheets);
+
+ return $worksheet;
+ }
+
+ /**
+ * @param Worksheet $worksheet
+ */
+ private function setCurrentWorksheet($worksheet)
+ {
+ $this->currentWorksheet = $worksheet;
+ }
+
+ /**
+ * Returns the worksheet associated to the given external sheet.
+ *
+ * @param Sheet $sheet
+ *
+ * @return null|Worksheet the worksheet associated to the given external sheet or null if not found
+ */
+ private function getWorksheetFromExternalSheet($sheet)
+ {
+ $worksheetFound = null;
+
+ foreach ($this->getWorksheets() as $worksheet) {
+ if ($worksheet->getExternalSheet() === $sheet) {
+ $worksheetFound = $worksheet;
+
+ break;
+ }
}
+
+ return $worksheetFound;
}
/**
- * Deletes the root folder created in the temp folder and all its contents.
+ * @return bool whether the current worksheet has reached the maximum number of rows per sheet
+ */
+ private function hasCurrentWorksheetReachedMaxRows()
+ {
+ $currentWorksheet = $this->getCurrentWorksheet();
+
+ return $currentWorksheet->getLastWrittenRowIndex() >= $this->getMaxRowsPerWorksheet();
+ }
+
+ /**
+ * Adds a row to the given sheet.
+ *
+ * @param Worksheet $worksheet Worksheet to write the row to
+ * @param Row $row The row to be added
*
- * @return void
+ * @throws IOException
+ * @throws \OpenSpout\Common\Exception\InvalidArgumentException
*/
- protected function cleanupTempFolder()
+ private function addRowToWorksheet(Worksheet $worksheet, Row $row)
{
- $rootFolder = $this->fileSystemHelper->getRootFolder();
- $this->fileSystemHelper->deleteFolderRecursively($rootFolder);
+ $this->applyDefaultRowStyle($row);
+ $this->worksheetManager->addRow($worksheet, $row);
+
+ // update max num columns for the worksheet
+ $currentMaxNumColumns = $worksheet->getMaxNumColumns();
+ $cellsCount = $row->getNumCells();
+ $worksheet->setMaxNumColumns(max($currentMaxNumColumns, $cellsCount));
+ }
+
+ private function applyDefaultRowStyle(Row $row)
+ {
+ $defaultRowStyle = $this->optionsManager->getOption(Options::DEFAULT_ROW_STYLE);
+
+ if (null !== $defaultRowStyle) {
+ $mergedStyle = $this->styleMerger->merge($row->getStyle(), $defaultRowStyle);
+ $row->setStyle($mergedStyle);
+ }
+ }
+
+ /**
+ * Closes all workbook's associated sheets.
+ */
+ private function closeAllWorksheets()
+ {
+ $worksheets = $this->getWorksheets();
+
+ foreach ($worksheets as $worksheet) {
+ $this->worksheetManager->close($worksheet);
+ }
}
}
diff --git a/lib/spout/src/Spout/Writer/Common/Manager/WorkbookManagerInterface.php b/lib/openspout/src/Writer/Common/Manager/WorkbookManagerInterface.php
similarity index 62%
rename from lib/spout/src/Spout/Writer/Common/Manager/WorkbookManagerInterface.php
rename to lib/openspout/src/Writer/Common/Manager/WorkbookManagerInterface.php
index aed304a02d87d..209abc6dc88cd 100644
--- a/lib/spout/src/Spout/Writer/Common/Manager/WorkbookManagerInterface.php
+++ b/lib/openspout/src/Writer/Common/Manager/WorkbookManagerInterface.php
@@ -1,14 +1,14 @@
entityFactory = $entityFactory;
@@ -36,7 +31,6 @@ public function __construct(InternalEntityFactory $entityFactory, HelperFactory
}
/**
- * @param OptionsManagerInterface $optionsManager
* @return WorkbookManager
*/
public function createWorkbookManager(OptionsManagerInterface $optionsManager)
@@ -63,41 +57,37 @@ public function createWorkbookManager(OptionsManagerInterface $optionsManager)
}
/**
- * @param StyleManager $styleManager
- * @param StyleMerger $styleMerger
- * @return WorksheetManager
+ * @return SheetManager
*/
- private function createWorksheetManager(StyleManager $styleManager, StyleMerger $styleMerger)
+ public function createSheetManager()
{
- $stringsEscaper = $this->helperFactory->createStringsEscaper();
- $stringsHelper = $this->helperFactory->createStringHelper();
+ $stringHelper = $this->helperFactory->createStringHelper();
- return new WorksheetManager($styleManager, $styleMerger, $stringsEscaper, $stringsHelper);
+ return new SheetManager($stringHelper);
}
/**
- * @return SheetManager
+ * @return WorksheetManager
*/
- public function createSheetManager()
+ private function createWorksheetManager(StyleManager $styleManager, StyleMerger $styleMerger)
{
- $stringHelper = $this->helperFactory->createStringHelper();
+ $stringsEscaper = $this->helperFactory->createStringsEscaper();
+ $stringsHelper = $this->helperFactory->createStringHelper();
- return new SheetManager($stringHelper);
+ return new WorksheetManager($styleManager, $styleMerger, $stringsEscaper, $stringsHelper);
}
/**
- * @param OptionsManagerInterface $optionsManager
* @return StyleManager
*/
private function createStyleManager(OptionsManagerInterface $optionsManager)
{
$styleRegistry = $this->createStyleRegistry($optionsManager);
- return new StyleManager($styleRegistry);
+ return new StyleManager($styleRegistry, $optionsManager);
}
/**
- * @param OptionsManagerInterface $optionsManager
* @return StyleRegistry
*/
private function createStyleRegistry(OptionsManagerInterface $optionsManager)
diff --git a/lib/spout/src/Spout/Writer/ODS/Helper/BorderHelper.php b/lib/openspout/src/Writer/ODS/Helper/BorderHelper.php
similarity index 63%
rename from lib/spout/src/Spout/Writer/ODS/Helper/BorderHelper.php
rename to lib/openspout/src/Writer/ODS/Helper/BorderHelper.php
index 34886acf411c5..caaf9544e2220 100644
--- a/lib/spout/src/Spout/Writer/ODS/Helper/BorderHelper.php
+++ b/lib/openspout/src/Writer/ODS/Helper/BorderHelper.php
@@ -1,16 +1,14 @@
'0.75pt',
+ Border::WIDTH_THIN => '0.75pt',
Border::WIDTH_MEDIUM => '1.75pt',
- Border::WIDTH_THICK => '2.5pt',
+ Border::WIDTH_THICK => '2.5pt',
];
/**
- * Style mapping
+ * Style mapping.
*
* @var array
*/
protected static $styleMap = [
- Border::STYLE_SOLID => 'solid',
+ Border::STYLE_SOLID => 'solid',
Border::STYLE_DASHED => 'dashed',
Border::STYLE_DOTTED => 'dotted',
Border::STYLE_DOUBLE => 'double',
];
/**
- * @param BorderPart $borderPart
* @return string
*/
public static function serializeBorderPart(BorderPart $borderPart)
{
$definition = 'fo:border-%s="%s"';
- if ($borderPart->getStyle() === Border::STYLE_NONE) {
- $borderPartDefinition = \sprintf($definition, $borderPart->getName(), 'none');
+ if (Border::STYLE_NONE === $borderPart->getStyle()) {
+ $borderPartDefinition = sprintf($definition, $borderPart->getName(), 'none');
} else {
$attributes = [
self::$widthMap[$borderPart->getWidth()],
self::$styleMap[$borderPart->getStyle()],
- '#' . $borderPart->getColor(),
+ '#'.$borderPart->getColor(),
];
- $borderPartDefinition = \sprintf($definition, $borderPart->getName(), \implode(' ', $attributes));
+ $borderPartDefinition = sprintf($definition, $borderPart->getName(), implode(' ', $attributes));
}
return $borderPartDefinition;
diff --git a/lib/spout/src/Spout/Writer/ODS/Helper/FileSystemHelper.php b/lib/openspout/src/Writer/ODS/Helper/FileSystemHelper.php
similarity index 57%
rename from lib/spout/src/Spout/Writer/ODS/Helper/FileSystemHelper.php
rename to lib/openspout/src/Writer/ODS/Helper/FileSystemHelper.php
index 5598bb4a55c6e..b3280b6a30364 100644
--- a/lib/spout/src/Spout/Writer/ODS/Helper/FileSystemHelper.php
+++ b/lib/openspout/src/Writer/ODS/Helper/FileSystemHelper.php
@@ -1,34 +1,30 @@
createMetaInfoFolderAndFile()
->createSheetsContentTempFolder()
->createMetaFile()
- ->createMimetypeFile();
+ ->createMimetypeFile()
+ ;
}
/**
- * Creates the folder that will be used as root
+ * Creates the "content.xml" file under the root folder.
+ *
+ * @param WorksheetManager $worksheetManager
+ * @param StyleManager $styleManager
+ * @param Worksheet[] $worksheets
*
- * @throws \Box\Spout\Common\Exception\IOException If unable to create the folder
* @return FileSystemHelper
*/
- protected function createRootFolder()
+ public function createContentFile($worksheetManager, $styleManager, $worksheets)
{
- $this->rootFolder = $this->createFolder($this->baseFolderRealPath, \uniqid('ods'));
+ $contentXmlFileContents = <<<'EOD'
+
+
+ EOD;
+
+ $contentXmlFileContents .= $styleManager->getContentXmlFontFaceSectionContent();
+ $contentXmlFileContents .= $styleManager->getContentXmlAutomaticStylesSectionContent($worksheets);
+
+ $contentXmlFileContents .= '';
+
+ $this->createFileWithContents($this->rootFolder, self::CONTENT_XML_FILE_NAME, $contentXmlFileContents);
+
+ // Append sheets content to "content.xml"
+ $contentXmlFilePath = $this->rootFolder.'/'.self::CONTENT_XML_FILE_NAME;
+ $contentXmlHandle = fopen($contentXmlFilePath, 'a');
+
+ foreach ($worksheets as $worksheet) {
+ // write the "" node, with the final sheet's name
+ fwrite($contentXmlHandle, $worksheetManager->getTableElementStartAsString($worksheet));
+
+ $worksheetFilePath = $worksheet->getFilePath();
+ $this->copyFileContentsToTarget($worksheetFilePath, $contentXmlHandle);
+
+ fwrite($contentXmlHandle, '');
+ }
+
+ $contentXmlFileContents = '';
+
+ fwrite($contentXmlHandle, $contentXmlFileContents);
+ fclose($contentXmlHandle);
return $this;
}
/**
- * Creates the "META-INF" folder under the root folder as well as the "manifest.xml" file in it
+ * Deletes the temporary folder where sheets content was stored.
*
- * @throws \Box\Spout\Common\Exception\IOException If unable to create the folder or the "manifest.xml" file
* @return FileSystemHelper
*/
- protected function createMetaInfoFolderAndFile()
+ public function deleteWorksheetTempFolder()
{
- $this->metaInfFolder = $this->createFolder($this->rootFolder, self::META_INF_FOLDER_NAME);
-
- $this->createManifestFile();
+ $this->deleteFolderRecursively($this->sheetsContentTempFolder);
return $this;
}
/**
- * Creates the "manifest.xml" file under the "META-INF" folder (under root)
+ * Creates the "styles.xml" file under the root folder.
+ *
+ * @param StyleManager $styleManager
+ * @param int $numWorksheets Number of created worksheets
*
- * @throws \Box\Spout\Common\Exception\IOException If unable to create the file
* @return FileSystemHelper
*/
- protected function createManifestFile()
+ public function createStylesFile($styleManager, $numWorksheets)
{
- $manifestXmlFileContents = <<<'EOD'
-
-
-
-
-
-
-
-EOD;
-
- $this->createFileWithContents($this->metaInfFolder, self::MANIFEST_XML_FILE_NAME, $manifestXmlFileContents);
+ $stylesXmlFileContents = $styleManager->getStylesXMLFileContent($numWorksheets);
+ $this->createFileWithContents($this->rootFolder, self::STYLES_XML_FILE_NAME, $stylesXmlFileContents);
return $this;
}
/**
- * Creates the temp folder where specific sheets content will be written to.
- * This folder is not part of the final ODS file and is only used to be able to jump between sheets.
+ * Zips the root folder and streams the contents of the zip into the given stream.
*
- * @throws \Box\Spout\Common\Exception\IOException If unable to create the folder
- * @return FileSystemHelper
+ * @param resource $streamPointer Pointer to the stream to copy the zip
*/
- protected function createSheetsContentTempFolder()
+ public function zipRootFolderAndCopyToStream($streamPointer)
{
- $this->sheetsContentTempFolder = $this->createFolder($this->rootFolder, self::SHEETS_CONTENT_TEMP_FOLDER_NAME);
+ $zip = $this->zipHelper->createZip($this->rootFolder);
- return $this;
+ $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.
+ // @see http://www.jejik.com/articles/2010/03/how_to_correctly_create_odf_documents_using_zip/
+ $this->zipHelper->addUncompressedFileToArchive($zip, $this->rootFolder, self::MIMETYPE_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 "meta.xml" file under the root folder
+ * Creates the folder that will be used as root.
+ *
+ * @throws \OpenSpout\Common\Exception\IOException If unable to create the folder
*
- * @throws \Box\Spout\Common\Exception\IOException If unable to create the file
* @return FileSystemHelper
*/
- protected function createMetaFile()
+ protected function createRootFolder()
{
- $appName = self::APP_NAME;
- $createdDate = (new \DateTime())->format(\DateTime::W3C);
-
- $metaXmlFileContents = <<
-
-
- $appName
- $createdDate
- $createdDate
-
-
-EOD;
-
- $this->createFileWithContents($this->rootFolder, self::META_XML_FILE_NAME, $metaXmlFileContents);
+ $this->rootFolder = $this->createFolder($this->baseFolderRealPath, uniqid('ods'));
return $this;
}
/**
- * Creates the "mimetype" file under the root folder
+ * Creates the "META-INF" folder under the root folder as well as the "manifest.xml" file in it.
+ *
+ * @throws \OpenSpout\Common\Exception\IOException If unable to create the folder or the "manifest.xml" file
*
- * @throws \Box\Spout\Common\Exception\IOException If unable to create the file
* @return FileSystemHelper
*/
- protected function createMimetypeFile()
+ protected function createMetaInfoFolderAndFile()
{
- $this->createFileWithContents($this->rootFolder, self::MIMETYPE_FILE_NAME, self::MIMETYPE);
+ $this->metaInfFolder = $this->createFolder($this->rootFolder, self::META_INF_FOLDER_NAME);
+
+ $this->createManifestFile();
return $this;
}
/**
- * Creates the "content.xml" file under the root folder
+ * Creates the "manifest.xml" file under the "META-INF" folder (under root).
+ *
+ * @throws \OpenSpout\Common\Exception\IOException If unable to create the file
*
- * @param WorksheetManager $worksheetManager
- * @param StyleManager $styleManager
- * @param Worksheet[] $worksheets
* @return FileSystemHelper
*/
- public function createContentFile($worksheetManager, $styleManager, $worksheets)
+ protected function createManifestFile()
{
- $contentXmlFileContents = <<<'EOD'
-
-
-EOD;
-
- $contentXmlFileContents .= $styleManager->getContentXmlFontFaceSectionContent();
- $contentXmlFileContents .= $styleManager->getContentXmlAutomaticStylesSectionContent($worksheets);
-
- $contentXmlFileContents .= '';
-
- $this->createFileWithContents($this->rootFolder, self::CONTENT_XML_FILE_NAME, $contentXmlFileContents);
-
- // Append sheets content to "content.xml"
- $contentXmlFilePath = $this->rootFolder . '/' . self::CONTENT_XML_FILE_NAME;
- $contentXmlHandle = \fopen($contentXmlFilePath, 'a');
-
- foreach ($worksheets as $worksheet) {
- // write the "" node, with the final sheet's name
- \fwrite($contentXmlHandle, $worksheetManager->getTableElementStartAsString($worksheet));
-
- $worksheetFilePath = $worksheet->getFilePath();
- $this->copyFileContentsToTarget($worksheetFilePath, $contentXmlHandle);
-
- \fwrite($contentXmlHandle, '');
- }
-
- $contentXmlFileContents = '';
+ $manifestXmlFileContents = <<<'EOD'
+
+
+
+
+
+
+
+ EOD;
- \fwrite($contentXmlHandle, $contentXmlFileContents);
- \fclose($contentXmlHandle);
+ $this->createFileWithContents($this->metaInfFolder, self::MANIFEST_XML_FILE_NAME, $manifestXmlFileContents);
return $this;
}
/**
- * Streams the content of the file at the given path into the target resource.
- * Depending on which mode the target resource was created with, it will truncate then copy
- * or append the content to the target file.
+ * Creates the temp folder where specific sheets content will be written to.
+ * This folder is not part of the final ODS file and is only used to be able to jump between sheets.
*
- * @param string $sourceFilePath Path of the file whose content will be copied
- * @param resource $targetResource Target resource that will receive the content
- * @return void
+ * @throws \OpenSpout\Common\Exception\IOException If unable to create the folder
+ *
+ * @return FileSystemHelper
*/
- protected function copyFileContentsToTarget($sourceFilePath, $targetResource)
+ protected function createSheetsContentTempFolder()
{
- $sourceHandle = \fopen($sourceFilePath, 'r');
- \stream_copy_to_stream($sourceHandle, $targetResource);
- \fclose($sourceHandle);
+ $this->sheetsContentTempFolder = $this->createFolder($this->rootFolder, self::SHEETS_CONTENT_TEMP_FOLDER_NAME);
+
+ return $this;
}
/**
- * Deletes the temporary folder where sheets content was stored.
+ * Creates the "meta.xml" file under the root folder.
+ *
+ * @throws \OpenSpout\Common\Exception\IOException If unable to create the file
*
* @return FileSystemHelper
*/
- public function deleteWorksheetTempFolder()
+ protected function createMetaFile()
{
- $this->deleteFolderRecursively($this->sheetsContentTempFolder);
+ $appName = self::APP_NAME;
+ $createdDate = (new \DateTime())->format(\DateTime::W3C);
+
+ $metaXmlFileContents = <<
+
+
+ {$appName}
+ {$createdDate}
+ {$createdDate}
+
+
+ EOD;
+
+ $this->createFileWithContents($this->rootFolder, self::META_XML_FILE_NAME, $metaXmlFileContents);
return $this;
}
/**
- * Creates the "styles.xml" file under the root folder
+ * Creates the "mimetype" file under the root folder.
+ *
+ * @throws \OpenSpout\Common\Exception\IOException If unable to create the file
*
- * @param StyleManager $styleManager
- * @param int $numWorksheets Number of created worksheets
* @return FileSystemHelper
*/
- public function createStylesFile($styleManager, $numWorksheets)
+ protected function createMimetypeFile()
{
- $stylesXmlFileContents = $styleManager->getStylesXMLFileContent($numWorksheets);
- $this->createFileWithContents($this->rootFolder, self::STYLES_XML_FILE_NAME, $stylesXmlFileContents);
+ $this->createFileWithContents($this->rootFolder, self::MIMETYPE_FILE_NAME, self::MIMETYPE);
return $this;
}
/**
- * Zips the root folder and streams the contents of the zip into the given stream
+ * Streams the content of the file at the given path into the target resource.
+ * Depending on which mode the target resource was created with, it will truncate then copy
+ * or append the content to the target file.
*
- * @param resource $streamPointer Pointer to the stream to copy the zip
- * @return void
+ * @param string $sourceFilePath Path of the file whose content will be copied
+ * @param resource $targetResource Target resource that will receive the content
*/
- public function zipRootFolderAndCopyToStream($streamPointer)
+ protected function copyFileContentsToTarget($sourceFilePath, $targetResource)
{
- $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.
- // @see http://www.jejik.com/articles/2010/03/how_to_correctly_create_odf_documents_using_zip/
- $this->zipHelper->addUncompressedFileToArchive($zip, $this->rootFolder, self::MIMETYPE_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);
+ $sourceHandle = fopen($sourceFilePath, 'r');
+ stream_copy_to_stream($sourceHandle, $targetResource);
+ fclose($sourceHandle);
}
}
diff --git a/lib/spout/src/Spout/Writer/ODS/Manager/OptionsManager.php b/lib/openspout/src/Writer/ODS/Manager/OptionsManager.php
similarity index 67%
rename from lib/spout/src/Spout/Writer/ODS/Manager/OptionsManager.php
rename to lib/openspout/src/Writer/ODS/Manager/OptionsManager.php
index a6fb564ef7926..d098abf96b12e 100644
--- a/lib/spout/src/Spout/Writer/ODS/Manager/OptionsManager.php
+++ b/lib/openspout/src/Writer/ODS/Manager/OptionsManager.php
@@ -1,14 +1,13 @@
setOption(Options::TEMP_FOLDER, \sys_get_temp_dir());
+ $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);
}
diff --git a/lib/openspout/src/Writer/ODS/Manager/Style/StyleManager.php b/lib/openspout/src/Writer/ODS/Manager/Style/StyleManager.php
new file mode 100644
index 0000000000000..e1d99764b6f95
--- /dev/null
+++ b/lib/openspout/src/Writer/ODS/Manager/Style/StyleManager.php
@@ -0,0 +1,462 @@
+setDefaultColumnWidth($optionsManager->getOption(Options::DEFAULT_COLUMN_WIDTH));
+ $this->setDefaultRowHeight($optionsManager->getOption(Options::DEFAULT_ROW_HEIGHT));
+ $this->columnWidths = $optionsManager->getOption(Options::COLUMN_WIDTHS) ?? [];
+ }
+
+ /**
+ * Returns the content of the "styles.xml" file, given a list of styles.
+ *
+ * @param int $numWorksheets Number of worksheets created
+ *
+ * @return string
+ */
+ public function getStylesXMLFileContent($numWorksheets)
+ {
+ $content = <<<'EOD'
+
+
+ EOD;
+
+ $content .= $this->getFontFaceSectionContent();
+ $content .= $this->getStylesSectionContent();
+ $content .= $this->getAutomaticStylesSectionContent($numWorksheets);
+ $content .= $this->getMasterStylesSectionContent($numWorksheets);
+
+ $content .= <<<'EOD'
+
+ EOD;
+
+ return $content;
+ }
+
+ /**
+ * Returns the contents of the "" section, inside "content.xml" file.
+ *
+ * @return string
+ */
+ public function getContentXmlFontFaceSectionContent()
+ {
+ $content = '';
+ foreach ($this->styleRegistry->getUsedFonts() as $fontName) {
+ $content .= '';
+ }
+ $content .= '';
+
+ return $content;
+ }
+
+ /**
+ * Returns the contents of the "" section, inside "content.xml" file.
+ *
+ * @param Worksheet[] $worksheets
+ *
+ * @return string
+ */
+ public function getContentXmlAutomaticStylesSectionContent($worksheets)
+ {
+ $content = '';
+
+ foreach ($this->styleRegistry->getRegisteredStyles() as $style) {
+ $content .= $this->getStyleSectionContent($style);
+ }
+
+ $useOptimalRowHeight = empty($this->defaultRowHeight) ? 'true' : 'false';
+ $defaultRowHeight = empty($this->defaultRowHeight) ? '15pt' : "{$this->defaultRowHeight}pt";
+ $defaultColumnWidth = empty($this->defaultColumnWidth) ? '' : "style:column-width=\"{$this->defaultColumnWidth}pt\"";
+
+ $content .= <<
+
+
+
+
+
+ EOD;
+
+ foreach ($worksheets as $worksheet) {
+ $worksheetId = $worksheet->getId();
+ $isSheetVisible = $worksheet->getExternalSheet()->isVisible() ? 'true' : 'false';
+
+ $content .= <<
+
+
+ EOD;
+ }
+
+ // Sort column widths since ODS cares about order
+ usort($this->columnWidths, function ($a, $b) {
+ if ($a[0] === $b[0]) {
+ return 0;
+ }
+
+ return ($a[0] < $b[0]) ? -1 : 1;
+ });
+ $content .= $this->getTableColumnStylesXMLContent();
+
+ $content .= '';
+
+ return $content;
+ }
+
+ public function getTableColumnStylesXMLContent(): string
+ {
+ if (empty($this->columnWidths)) {
+ return '';
+ }
+
+ $content = '';
+ foreach ($this->columnWidths as $styleIndex => $entry) {
+ $content .= <<
+
+
+ EOD;
+ }
+
+ return $content;
+ }
+
+ public function getStyledTableColumnXMLContent(int $maxNumColumns): string
+ {
+ if (empty($this->columnWidths)) {
+ return '';
+ }
+
+ $content = '';
+ foreach ($this->columnWidths as $styleIndex => $entry) {
+ $numCols = $entry[1] - $entry[0] + 1;
+ $content .= <<
+ EOD;
+ }
+ // Note: This assumes the column widths are contiguous and default width is
+ // only applied to columns after the last custom column with a custom width
+ $content .= '';
+
+ return $content;
+ }
+
+ /**
+ * Returns the content of the "" section, inside "styles.xml" file.
+ *
+ * @return string
+ */
+ protected function getFontFaceSectionContent()
+ {
+ $content = '';
+ foreach ($this->styleRegistry->getUsedFonts() as $fontName) {
+ $content .= '';
+ }
+ $content .= '';
+
+ return $content;
+ }
+
+ /**
+ * Returns the content of the "" section, inside "styles.xml" file.
+ *
+ * @return string
+ */
+ protected function getStylesSectionContent()
+ {
+ $defaultStyle = $this->getDefaultStyle();
+
+ return <<
+
+
+
+
+
+
+
+
+ EOD;
+ }
+
+ /**
+ * Returns the content of the "" section, inside "styles.xml" file.
+ *
+ * @param int $numWorksheets Number of worksheets created
+ *
+ * @return string
+ */
+ protected function getAutomaticStylesSectionContent($numWorksheets)
+ {
+ $content = '';
+
+ for ($i = 1; $i <= $numWorksheets; ++$i) {
+ $content .= <<
+
+
+
+
+ EOD;
+ }
+
+ $content .= '';
+
+ return $content;
+ }
+
+ /**
+ * Returns the content of the "" section, inside "styles.xml" file.
+ *
+ * @param int $numWorksheets Number of worksheets created
+ *
+ * @return string
+ */
+ protected function getMasterStylesSectionContent($numWorksheets)
+ {
+ $content = '';
+
+ for ($i = 1; $i <= $numWorksheets; ++$i) {
+ $content .= <<
+
+
+
+
+
+ EOD;
+ }
+
+ $content .= '';
+
+ return $content;
+ }
+
+ /**
+ * Returns the contents of the "" section, inside "" section.
+ *
+ * @param \OpenSpout\Common\Entity\Style\Style $style
+ *
+ * @return string
+ */
+ protected function getStyleSectionContent($style)
+ {
+ $styleIndex = $style->getId() + 1; // 1-based
+
+ $content = '';
+
+ $content .= $this->getTextPropertiesSectionContent($style);
+ $content .= $this->getParagraphPropertiesSectionContent($style);
+ $content .= $this->getTableCellPropertiesSectionContent($style);
+
+ $content .= '';
+
+ return $content;
+ }
+
+ /**
+ * Returns the contents of the "" section, inside "" section.
+ *
+ * @param \OpenSpout\Common\Entity\Style\Style $style
+ *
+ * @return string
+ */
+ private function getTextPropertiesSectionContent($style)
+ {
+ if (!$style->shouldApplyFont()) {
+ return '';
+ }
+
+ return 'getFontSectionContent($style)
+ .'/>';
+ }
+
+ /**
+ * Returns the contents of the fonts definition section, inside "" section.
+ *
+ * @param \OpenSpout\Common\Entity\Style\Style $style
+ *
+ * @return string
+ */
+ private function getFontSectionContent($style)
+ {
+ $defaultStyle = $this->getDefaultStyle();
+ $content = '';
+
+ $fontColor = $style->getFontColor();
+ if ($fontColor !== $defaultStyle->getFontColor()) {
+ $content .= ' fo:color="#'.$fontColor.'"';
+ }
+
+ $fontName = $style->getFontName();
+ if ($fontName !== $defaultStyle->getFontName()) {
+ $content .= ' style:font-name="'.$fontName.'" style:font-name-asian="'.$fontName.'" style:font-name-complex="'.$fontName.'"';
+ }
+
+ $fontSize = $style->getFontSize();
+ if ($fontSize !== $defaultStyle->getFontSize()) {
+ $content .= ' fo:font-size="'.$fontSize.'pt" style:font-size-asian="'.$fontSize.'pt" style:font-size-complex="'.$fontSize.'pt"';
+ }
+
+ if ($style->isFontBold()) {
+ $content .= ' fo:font-weight="bold" style:font-weight-asian="bold" style:font-weight-complex="bold"';
+ }
+ if ($style->isFontItalic()) {
+ $content .= ' fo:font-style="italic" style:font-style-asian="italic" style:font-style-complex="italic"';
+ }
+ if ($style->isFontUnderline()) {
+ $content .= ' style:text-underline-style="solid" style:text-underline-type="single"';
+ }
+ if ($style->isFontStrikethrough()) {
+ $content .= ' style:text-line-through-style="solid"';
+ }
+
+ return $content;
+ }
+
+ /**
+ * Returns the contents of the "" section, inside "" section.
+ *
+ * @param \OpenSpout\Common\Entity\Style\Style $style
+ *
+ * @return string
+ */
+ private function getParagraphPropertiesSectionContent($style)
+ {
+ if (!$style->shouldApplyCellAlignment()) {
+ return '';
+ }
+
+ return 'getCellAlignmentSectionContent($style)
+ .'/>';
+ }
+
+ /**
+ * Returns the contents of the cell alignment definition for the "" section.
+ *
+ * @param \OpenSpout\Common\Entity\Style\Style $style
+ *
+ * @return string
+ */
+ private function getCellAlignmentSectionContent($style)
+ {
+ return sprintf(
+ ' fo:text-align="%s" ',
+ $this->transformCellAlignment($style->getCellAlignment())
+ );
+ }
+
+ /**
+ * Even though "left" and "right" alignments are part of the spec, and interpreted
+ * respectively as "start" and "end", using the recommended values increase compatibility
+ * with software that will read the created ODS file.
+ *
+ * @param string $cellAlignment
+ *
+ * @return string
+ */
+ private function transformCellAlignment($cellAlignment)
+ {
+ switch ($cellAlignment) {
+ case CellAlignment::LEFT:
+ return 'start';
+
+ case CellAlignment::RIGHT:
+ return 'end';
+
+ default:
+ return $cellAlignment;
+ }
+ }
+
+ /**
+ * Returns the contents of the "" section, inside "" section.
+ *
+ * @param \OpenSpout\Common\Entity\Style\Style $style
+ *
+ * @return string
+ */
+ private function getTableCellPropertiesSectionContent($style)
+ {
+ $content = 'shouldWrapText()) {
+ $content .= $this->getWrapTextXMLContent();
+ }
+
+ if ($style->shouldApplyBorder()) {
+ $content .= $this->getBorderXMLContent($style);
+ }
+
+ if ($style->shouldApplyBackgroundColor()) {
+ $content .= $this->getBackgroundColorXMLContent($style);
+ }
+
+ $content .= '/>';
+
+ return $content;
+ }
+
+ /**
+ * Returns the contents of the wrap text definition for the "" section.
+ *
+ * @return string
+ */
+ private function getWrapTextXMLContent()
+ {
+ return ' fo:wrap-option="wrap" style:vertical-align="automatic" ';
+ }
+
+ /**
+ * Returns the contents of the borders definition for the "" section.
+ *
+ * @param \OpenSpout\Common\Entity\Style\Style $style
+ *
+ * @return string
+ */
+ private function getBorderXMLContent($style)
+ {
+ $borders = array_map(function (BorderPart $borderPart) {
+ return BorderHelper::serializeBorderPart($borderPart);
+ }, $style->getBorder()->getParts());
+
+ return sprintf(' %s ', implode(' ', $borders));
+ }
+
+ /**
+ * Returns the contents of the background color definition for the "" section.
+ *
+ * @param \OpenSpout\Common\Entity\Style\Style $style
+ *
+ * @return string
+ */
+ private function getBackgroundColorXMLContent($style)
+ {
+ return sprintf(' fo:background-color="#%s" ', $style->getBackgroundColor());
+ }
+}
diff --git a/lib/spout/src/Spout/Writer/ODS/Manager/Style/StyleRegistry.php b/lib/openspout/src/Writer/ODS/Manager/Style/StyleRegistry.php
similarity index 69%
rename from lib/spout/src/Spout/Writer/ODS/Manager/Style/StyleRegistry.php
rename to lib/openspout/src/Writer/ODS/Manager/Style/StyleRegistry.php
index 42484f2929435..e90dc96b986e0 100644
--- a/lib/spout/src/Spout/Writer/ODS/Manager/Style/StyleRegistry.php
+++ b/lib/openspout/src/Writer/ODS/Manager/Style/StyleRegistry.php
@@ -1,14 +1,13 @@
[] Map whose keys contain all the fonts used */
protected $usedFontsSet = [];
@@ -18,7 +17,8 @@ class StyleRegistry extends \Box\Spout\Writer\Common\Manager\Style\StyleRegistry
* Duplicate styles won't be registered more than once.
*
* @param Style $style The style to be registered
- * @return Style The registered style, updated with an internal ID.
+ *
+ * @return Style the registered style, updated with an internal ID
*/
public function registerStyle(Style $style)
{
@@ -37,6 +37,6 @@ public function registerStyle(Style $style)
*/
public function getUsedFonts()
{
- return \array_keys($this->usedFontsSet);
+ return array_keys($this->usedFontsSet);
}
}
diff --git a/lib/spout/src/Spout/Writer/ODS/Manager/WorkbookManager.php b/lib/openspout/src/Writer/ODS/Manager/WorkbookManager.php
similarity index 76%
rename from lib/spout/src/Spout/Writer/ODS/Manager/WorkbookManager.php
rename to lib/openspout/src/Writer/ODS/Manager/WorkbookManager.php
index 77c5f90373bec..90dc0e4ace793 100644
--- a/lib/spout/src/Spout/Writer/ODS/Manager/WorkbookManager.php
+++ b/lib/openspout/src/Writer/ODS/Manager/WorkbookManager.php
@@ -1,20 +1,20 @@
fileSystemHelper->getSheetsContentTempFolder();
+
+ return $sheetsContentTempFolder.'/sheet'.$sheet->getIndex().'.xml';
}
/**
- * @param Sheet $sheet
- * @return string The file path where the data for the given sheet will be stored
+ * @return int Maximum number of rows/columns a sheet can contain
*/
- public function getWorksheetFilePath(Sheet $sheet)
+ protected function getMaxRowsPerWorksheet()
{
- $sheetsContentTempFolder = $this->fileSystemHelper->getSheetsContentTempFolder();
-
- return $sheetsContentTempFolder . '/sheet' . $sheet->getIndex() . '.xml';
+ return self::$maxRowsPerWorksheet;
}
/**
* 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
- * @return void
*/
protected function writeAllFilesToDiskAndZipThem($finalFilePointer)
{
@@ -62,6 +60,7 @@ protected function writeAllFilesToDiskAndZipThem($finalFilePointer)
->createContentFile($this->worksheetManager, $this->styleManager, $worksheets)
->deleteWorksheetTempFolder()
->createStylesFile($this->styleManager, $numWorksheets)
- ->zipRootFolderAndCopyToStream($finalFilePointer);
+ ->zipRootFolderAndCopyToStream($finalFilePointer)
+ ;
}
}
diff --git a/lib/spout/src/Spout/Writer/ODS/Manager/WorksheetManager.php b/lib/openspout/src/Writer/ODS/Manager/WorksheetManager.php
similarity index 57%
rename from lib/spout/src/Spout/Writer/ODS/Manager/WorksheetManager.php
rename to lib/openspout/src/Writer/ODS/Manager/WorksheetManager.php
index 7d7cb0ebb92e1..3eeca5f27282d 100644
--- a/lib/spout/src/Spout/Writer/ODS/Manager/WorksheetManager.php
+++ b/lib/openspout/src/Writer/ODS/Manager/WorksheetManager.php
@@ -1,27 +1,26 @@
getFilePath(), 'w');
+ $sheetFilePointer = fopen($worksheet->getFilePath(), 'w');
$this->throwIfSheetFilePointerIsNotAvailable($sheetFilePointer);
$worksheet->setFilePointer($sheetFilePointer);
}
- /**
- * 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
- * @return void
- */
- private function throwIfSheetFilePointerIsNotAvailable($sheetFilePointer)
- {
- if (!$sheetFilePointer) {
- throw new IOException('Unable to open sheet for writing.');
- }
- }
-
/**
* Returns the table XML root node as string.
*
- * @param Worksheet $worksheet
- * @return string node as string
+ * @return string "" node as string
*/
public function getTableElementStartAsString(Worksheet $worksheet)
{
$externalSheet = $worksheet->getExternalSheet();
$escapedSheetName = $this->stringsEscaper->escape($externalSheet->getName());
- $tableStyleName = 'ta' . ($externalSheet->getIndex() + 1);
+ $tableStyleName = 'ta'.($externalSheet->getIndex() + 1);
- $tableElement = '';
- $tableElement .= '';
+ $tableElement = '';
+ $tableElement .= $this->styleManager->getStyledTableColumnXMLContent($worksheet->getMaxNumColumns());
return $tableElement;
}
@@ -104,10 +83,10 @@ public function getTableElementStartAsString(Worksheet $worksheet)
* Adds a row to the given worksheet.
*
* @param Worksheet $worksheet The worksheet to add the row to
- * @param Row $row The row to be added
+ * @param Row $row The row to be added
+ *
* @throws InvalidArgumentException If a cell value's type is not supported
- * @throws IOException If the data cannot be written
- * @return void
+ * @throws IOException If the data cannot be written
*/
public function addRow(Worksheet $worksheet, Row $row)
{
@@ -119,13 +98,13 @@ public function addRow(Worksheet $worksheet, Row $row)
$currentCellIndex = 0;
$nextCellIndex = 1;
- for ($i = 0; $i < $row->getNumCells(); $i++) {
+ for ($i = 0; $i < $row->getNumCells(); ++$i) {
/** @var Cell $cell */
$cell = $cells[$currentCellIndex];
- /** @var Cell|null $nextCell */
- $nextCell = isset($cells[$nextCellIndex]) ? $cells[$nextCellIndex] : null;
+ /** @var null|Cell $nextCell */
+ $nextCell = $cells[$nextCellIndex] ?? null;
- if ($nextCell === null || $cell->getValue() !== $nextCell->getValue()) {
+ if (null === $nextCell || $cell->getValue() !== $nextCell->getValue()) {
$registeredStyle = $this->applyStyleAndRegister($cell, $rowStyle);
$cellStyle = $registeredStyle->getStyle();
if ($registeredStyle->isMatchingRowStyle()) {
@@ -136,13 +115,13 @@ public function addRow(Worksheet $worksheet, Row $row)
$currentCellIndex = $nextCellIndex;
}
- $nextCellIndex++;
+ ++$nextCellIndex;
}
$data .= '';
- $wasWriteSuccessful = \fwrite($worksheet->getFilePointer(), $data);
- if ($wasWriteSuccessful === false) {
+ $wasWriteSuccessful = fwrite($worksheet->getFilePointer(), $data);
+ if (false === $wasWriteSuccessful) {
throw new IOException("Unable to write data in {$worksheet->getFilePath()}");
}
@@ -152,14 +131,73 @@ public function addRow(Worksheet $worksheet, Row $row)
}
/**
- * Applies styles to the given style, merging the cell's style with its row's style
+ * Closes the worksheet.
+ */
+ public function close(Worksheet $worksheet)
+ {
+ $worksheetFilePointer = $worksheet->getFilePointer();
+
+ if (!\is_resource($worksheetFilePointer)) {
+ return;
+ }
+
+ fclose($worksheetFilePointer);
+ }
+
+ /**
+ * @param null|float $width
+ */
+ public function setDefaultColumnWidth($width)
+ {
+ $this->styleManager->setDefaultColumnWidth($width);
+ }
+
+ /**
+ * @param null|float $height
+ */
+ public function setDefaultRowHeight($height)
+ {
+ $this->styleManager->setDefaultRowHeight($height);
+ }
+
+ /**
+ * @param int ...$columns One or more columns with this width
+ */
+ public function setColumnWidth(float $width, ...$columns)
+ {
+ $this->styleManager->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
+ */
+ public function setColumnWidthForRange(float $width, int $start, int $end)
+ {
+ $this->styleManager->setColumnWidthForRange($width, $start, $end);
+ }
+
+ /**
+ * 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.');
+ }
+ }
+
+ /**
+ * Applies styles to the given style, merging the cell's style with its row's style.
*
- * @param Cell $cell
- * @param Style $rowStyle
* @throws InvalidArgumentException If a cell value's type is not supported
- * @return RegisteredStyle
*/
- private function applyStyleAndRegister(Cell $cell, Style $rowStyle) : RegisteredStyle
+ private function applyStyleAndRegister(Cell $cell, Style $rowStyle): RegisteredStyle
{
$isMatchingRowStyle = false;
if ($cell->getStyle()->isEmpty()) {
@@ -190,7 +228,7 @@ private function applyStyleAndRegister(Cell $cell, Style $rowStyle) : Registered
return new RegisteredStyle($registeredStyle, $isMatchingRowStyle);
}
- private function getCellXMLWithStyle(Cell $cell, Style $style, int $currentCellIndex, int $nextCellIndex) : string
+ private function getCellXMLWithStyle(Cell $cell, Style $style, int $currentCellIndex, int $nextCellIndex): string
{
$styleIndex = $style->getId() + 1; // 1-based
@@ -202,67 +240,69 @@ private function getCellXMLWithStyle(Cell $cell, Style $style, int $currentCellI
/**
* Returns the cell XML content, given its value.
*
- * @param Cell $cell The cell to be written
- * @param int $styleIndex Index of the used style
- * @param int $numTimesValueRepeated Number of times the value is consecutively repeated
+ * @param Cell $cell The cell to be written
+ * @param int $styleIndex Index of the used style
+ * @param int $numTimesValueRepeated Number of times the value is consecutively repeated
+ *
* @throws InvalidArgumentException If a cell value's type is not supported
+ *
* @return string The cell XML content
*/
private function getCellXML(Cell $cell, $styleIndex, $numTimesValueRepeated)
{
- $data = 'isString()) {
$data .= ' office:value-type="string" calcext:value-type="string">';
- $cellValueLines = \explode("\n", $cell->getValue());
+ $cellValueLines = explode("\n", $cell->getValue());
foreach ($cellValueLines as $cellValueLine) {
- $data .= '' . $this->stringsEscaper->escape($cellValueLine) . '';
+ $data .= ''.$this->stringsEscaper->escape($cellValueLine).'';
}
$data .= '';
} elseif ($cell->isBoolean()) {
$value = $cell->getValue() ? 'true' : 'false'; // boolean-value spec: http://docs.oasis-open.org/office/v1.2/os/OpenDocument-v1.2-os-part1.html#datatype-boolean
- $data .= ' office:value-type="boolean" calcext:value-type="boolean" office:boolean-value="' . $value . '">';
- $data .= '' . $cell->getValue() . '';
+ $data .= ' office:value-type="boolean" calcext:value-type="boolean" office:boolean-value="'.$value.'">';
+ $data .= ''.$cell->getValue().'';
$data .= '';
} elseif ($cell->isNumeric()) {
$cellValue = $this->stringHelper->formatNumericValue($cell->getValue());
- $data .= ' office:value-type="float" calcext:value-type="float" office:value="' . $cellValue . '">';
- $data .= '' . $cellValue . '';
+ $data .= ' office:value-type="float" calcext:value-type="float" office:value="'.$cellValue.'">';
+ $data .= ''.$cellValue.'';
+ $data .= '';
+ } elseif ($cell->isDate()) {
+ $value = $cell->getValue();
+ if ($value instanceof \DateTimeInterface) {
+ $datevalue = substr((new \DateTimeImmutable('@'.$value->getTimestamp()))->format(\DateTimeInterface::W3C), 0, -6);
+ $data .= ' office:value-type="date" calcext:value-type="date" office:date-value="'.$datevalue.'Z">';
+ $data .= ''.$datevalue.'Z';
+ } elseif ($value instanceof \DateInterval) {
+ // workaround for missing DateInterval::format('c'), see https://stackoverflow.com/a/61088115/53538
+ static $f = ['M0S', 'H0M', 'DT0H', 'M0D', 'Y0M', 'P0Y', 'Y0M', 'P0M'];
+ static $r = ['M', 'H', 'DT', 'M', 'Y0M', 'P', 'Y', 'P'];
+ $value = rtrim(str_replace($f, $r, $value->format('P%yY%mM%dDT%hH%iM%sS')), 'PT') ?: 'PT0S';
+ $data .= ' office:value-type="time" office:time-value="'.$value.'">';
+ $data .= ''.$value.'';
+ } else {
+ throw new InvalidArgumentException('Trying to add a date value with an unsupported type: '.\gettype($cell->getValue()));
+ }
$data .= '';
- } elseif ($cell->isError() && is_string($cell->getValueEvenIfError())) {
+ } elseif ($cell->isError() && \is_string($cell->getValueEvenIfError())) {
// only writes the error value if it's a string
$data .= ' office:value-type="string" calcext:value-type="error" office:value="">';
- $data .= '' . $cell->getValueEvenIfError() . '';
+ $data .= ''.$cell->getValueEvenIfError().'';
$data .= '';
} elseif ($cell->isEmpty()) {
$data .= '/>';
} else {
- throw new InvalidArgumentException('Trying to add a value with an unsupported type: ' . \gettype($cell->getValue()));
+ throw new InvalidArgumentException('Trying to add a value with an unsupported type: '.\gettype($cell->getValue()));
}
return $data;
}
-
- /**
- * Closes the worksheet
- *
- * @param Worksheet $worksheet
- * @return void
- */
- public function close(Worksheet $worksheet)
- {
- $worksheetFilePointer = $worksheet->getFilePointer();
-
- if (!\is_resource($worksheetFilePointer)) {
- return;
- }
-
- \fclose($worksheetFilePointer);
- }
}
diff --git a/lib/spout/src/Spout/Writer/ODS/Writer.php b/lib/openspout/src/Writer/ODS/Writer.php
similarity index 74%
rename from lib/spout/src/Spout/Writer/ODS/Writer.php
rename to lib/openspout/src/Writer/ODS/Writer.php
index f016d57876a96..5f510e636e587 100644
--- a/lib/spout/src/Spout/Writer/ODS/Writer.php
+++ b/lib/openspout/src/Writer/ODS/Writer.php
@@ -1,13 +1,12 @@
helperFactory = $helperFactory;
}
- /**
- * Opens the streamer and makes it ready to accept data.
- *
- * @throws IOException If the writer cannot be opened
- * @return void
- */
- 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
- * @return void
- */
- abstract protected function addRowToWriter(Row $row);
-
- /**
- * Closes the streamer, preventing any additional writing.
- *
- * @return void
- */
- abstract protected function closeWriter();
-
/**
* {@inheritdoc}
*/
@@ -137,11 +102,11 @@ public function openToBrowser($outputFileName)
* @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-Type: '.static::$headerContentType);
$this->globalFunctionsHelper->header(
- 'Content-Disposition: attachment; ' .
- 'filename="' . rawurlencode($this->outputFilePath) . '"; ' .
- 'filename*=UTF-8\'\'' . rawurlencode($this->outputFilePath)
+ 'Content-Disposition: attachment; '.
+ 'filename="'.rawurlencode($this->outputFilePath).'"; '.
+ 'filename*=UTF-8\'\''.rawurlencode($this->outputFilePath)
);
/*
@@ -160,35 +125,6 @@ public function openToBrowser($outputFileName)
return $this;
}
- /**
- * 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
- * @return void
- */
- protected function throwIfFilePointerIsNotAvailable()
- {
- if (!$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.
- * @return void
- */
- protected function throwIfWriterAlreadyOpened($message)
- {
- if ($this->isWriterOpened) {
- throw new WriterAlreadyOpenedException($message);
- }
- }
-
/**
* {@inheritdoc}
*/
@@ -220,6 +156,7 @@ 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');
}
@@ -247,11 +184,59 @@ public function close()
$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).
- *
- * @return void
*/
private function closeAndAttemptToCleanupAllFiles()
{
diff --git a/lib/spout/src/Spout/Writer/WriterInterface.php b/lib/openspout/src/Writer/WriterInterface.php
similarity index 63%
rename from lib/spout/src/Spout/Writer/WriterInterface.php
rename to lib/openspout/src/Writer/WriterInterface.php
index 9f77e49c537b5..71a298e6a73c9 100644
--- a/lib/spout/src/Spout/Writer/WriterInterface.php
+++ b/lib/openspout/src/Writer/WriterInterface.php
@@ -1,12 +1,12 @@
throwIfWriterAlreadyOpened('Writer must be configured before opening it.');
- $this->optionsManager->setOption(Options::SHOULD_CREATE_NEW_SHEETS_AUTOMATICALLY, $shouldCreateNewSheetsAutomatically);
+ $this->optionsManager->setOption(
+ Options::SHOULD_CREATE_NEW_SHEETS_AUTOMATICALLY,
+ $shouldCreateNewSheetsAutomatically
+ );
return $this;
}
/**
- * {@inheritdoc}
- */
- protected function openWriter()
- {
- if (!$this->workbookManager) {
- $this->workbookManager = $this->managerFactory->createWorkbookManager($this->optionsManager);
- $this->workbookManager->addNewSheetAndMakeItCurrent();
- }
- }
-
- /**
- * Returns all the workbook's sheets
+ * 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()
@@ -85,7 +69,6 @@ public function getSheets()
$externalSheets = [];
$worksheets = $this->workbookManager->getWorksheets();
- /** @var Worksheet $worksheet */
foreach ($worksheets as $worksheet) {
$externalSheets[] = $worksheet->getExternalSheet();
}
@@ -96,7 +79,9 @@ public function getSheets()
/**
* 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()
@@ -108,9 +93,10 @@ public function addNewSheetAndMakeItCurrent()
}
/**
- * Returns the current sheet
+ * Returns the current sheet.
*
* @throws WriterNotOpenedException If the writer has not been opened yet
+ *
* @return Sheet The current sheet
*/
public function getCurrentSheet()
@@ -125,9 +111,9 @@ public function getCurrentSheet()
* 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
- * @throws SheetNotFoundException If the given sheet does not exist in the workbook
- * @return void
*/
public function setCurrentSheet($sheet)
{
@@ -135,11 +121,70 @@ public function setCurrentSheet($sheet)
$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
- * @return void
*/
protected function throwIfWorkbookIsNotAvailable()
{
@@ -150,6 +195,8 @@ protected function throwIfWorkbookIsNotAvailable()
/**
* {@inheritdoc}
+ *
+ * @throws Exception\WriterException
*/
protected function addRowToWriter(Row $row)
{
@@ -162,7 +209,7 @@ protected function addRowToWriter(Row $row)
*/
protected function closeWriter()
{
- if ($this->workbookManager) {
+ if (null !== $this->workbookManager) {
$this->workbookManager->close($this->filePointer);
}
}
diff --git a/lib/spout/src/Spout/Writer/XLSX/Creator/HelperFactory.php b/lib/openspout/src/Writer/XLSX/Creator/HelperFactory.php
similarity index 57%
rename from lib/spout/src/Spout/Writer/XLSX/Creator/HelperFactory.php
rename to lib/openspout/src/Writer/XLSX/Creator/HelperFactory.php
index e295170b3a1b1..38a7f0daf6abf 100644
--- a/lib/spout/src/Spout/Writer/XLSX/Creator/HelperFactory.php
+++ b/lib/openspout/src/Writer/XLSX/Creator/HelperFactory.php
@@ -1,24 +1,21 @@
entityFactory = $entityFactory;
@@ -38,7 +33,6 @@ public function __construct(InternalEntityFactory $entityFactory, HelperFactory
}
/**
- * @param OptionsManagerInterface $optionsManager
* @return WorkbookManager
*/
public function createWorkbookManager(OptionsManagerInterface $optionsManager)
@@ -68,10 +62,24 @@ public function createWorkbookManager(OptionsManagerInterface $optionsManager)
}
/**
- * @param OptionsManagerInterface $optionsManager
- * @param StyleManager $styleManager
- * @param StyleMerger $styleMerger
- * @param SharedStringsManager $sharedStringsManager
+ * @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(
@@ -91,31 +99,11 @@ private function createWorksheetManager(
$styleMerger,
$sharedStringsManager,
$stringsEscaper,
- $stringsHelper,
- $this->entityFactory
+ $stringsHelper
);
}
/**
- * @return SheetManager
- */
- public function createSheetManager()
- {
- $stringHelper = $this->helperFactory->createStringHelper();
-
- return new SheetManager($stringHelper);
- }
-
- /**
- * @return RowManager
- */
- public function createRowManager()
- {
- return new RowManager();
- }
-
- /**
- * @param OptionsManagerInterface $optionsManager
* @return StyleManager
*/
private function createStyleManager(OptionsManagerInterface $optionsManager)
@@ -126,7 +114,6 @@ private function createStyleManager(OptionsManagerInterface $optionsManager)
}
/**
- * @param OptionsManagerInterface $optionsManager
* @return StyleRegistry
*/
private function createStyleRegistry(OptionsManagerInterface $optionsManager)
@@ -146,6 +133,7 @@ private function createStyleMerger()
/**
* @param string $xlFolder Path to the "xl" folder
+ *
* @return SharedStringsManager
*/
private function createSharedStringsManager($xlFolder)
diff --git a/lib/openspout/src/Writer/XLSX/Entity/SheetView.php b/lib/openspout/src/Writer/XLSX/Entity/SheetView.php
new file mode 100644
index 0000000000000..71416159242ff
--- /dev/null
+++ b/lib/openspout/src/Writer/XLSX/Entity/SheetView.php
@@ -0,0 +1,289 @@
+showFormulas = $showFormulas;
+
+ return $this;
+ }
+
+ /**
+ * @return $this
+ */
+ public function setShowGridLines(bool $showGridLines): self
+ {
+ $this->showGridLines = $showGridLines;
+
+ return $this;
+ }
+
+ /**
+ * @return $this
+ */
+ public function setShowRowColHeaders(bool $showRowColHeaders): self
+ {
+ $this->showRowColHeaders = $showRowColHeaders;
+
+ return $this;
+ }
+
+ /**
+ * @return $this
+ */
+ public function setShowZeroes(bool $showZeroes): self
+ {
+ $this->showZeroes = $showZeroes;
+
+ return $this;
+ }
+
+ /**
+ * @return $this
+ */
+ public function setRightToLeft(bool $rightToLeft): self
+ {
+ $this->rightToLeft = $rightToLeft;
+
+ return $this;
+ }
+
+ /**
+ * @return $this
+ */
+ public function setTabSelected(bool $tabSelected): self
+ {
+ $this->tabSelected = $tabSelected;
+
+ return $this;
+ }
+
+ /**
+ * @return $this
+ */
+ public function setShowOutlineSymbols(bool $showOutlineSymbols): self
+ {
+ $this->showOutlineSymbols = $showOutlineSymbols;
+
+ return $this;
+ }
+
+ /**
+ * @return $this
+ */
+ public function setDefaultGridColor(bool $defaultGridColor): self
+ {
+ $this->defaultGridColor = $defaultGridColor;
+
+ return $this;
+ }
+
+ /**
+ * @return $this
+ */
+ public function setView(string $view): self
+ {
+ $this->view = $view;
+
+ return $this;
+ }
+
+ /**
+ * @return $this
+ */
+ public function setTopLeftCell(string $topLeftCell): self
+ {
+ $this->topLeftCell = $topLeftCell;
+
+ return $this;
+ }
+
+ /**
+ * @return $this
+ */
+ public function setColorId(int $colorId): self
+ {
+ $this->colorId = $colorId;
+
+ return $this;
+ }
+
+ /**
+ * @return $this
+ */
+ public function setZoomScale(int $zoomScale): self
+ {
+ $this->zoomScale = $zoomScale;
+
+ return $this;
+ }
+
+ /**
+ * @return $this
+ */
+ public function setZoomScaleNormal(int $zoomScaleNormal): self
+ {
+ $this->zoomScaleNormal = $zoomScaleNormal;
+
+ return $this;
+ }
+
+ /**
+ * @return $this
+ */
+ public function setZoomScalePageLayoutView(int $zoomScalePageLayoutView): self
+ {
+ $this->zoomScalePageLayoutView = $zoomScalePageLayoutView;
+
+ return $this;
+ }
+
+ /**
+ * @return $this
+ */
+ public function setWorkbookViewId(int $workbookViewId): self
+ {
+ $this->workbookViewId = $workbookViewId;
+
+ return $this;
+ }
+
+ /**
+ * @param int $freezeRow Set to 2 to fix the first row
+ *
+ * @return $this
+ */
+ public function setFreezeRow(int $freezeRow): self
+ {
+ if ($freezeRow < 1) {
+ throw new InvalidArgumentException('Freeze row must be a positive integer', 1589543073);
+ }
+
+ $this->freezeRow = $freezeRow;
+
+ return $this;
+ }
+
+ /**
+ * @param string $freezeColumn Set to B to fix the first column
+ *
+ * @return $this
+ */
+ public function setFreezeColumn(string $freezeColumn): self
+ {
+ $this->freezeColumn = strtoupper($freezeColumn);
+
+ return $this;
+ }
+
+ public function getXml(): string
+ {
+ return 'getSheetViewAttributes().'>'.
+ $this->getFreezeCellPaneXml().
+ '';
+ }
+
+ protected function getSheetViewAttributes(): string
+ {
+ // Get class properties
+ $propertyValues = get_object_vars($this);
+ unset($propertyValues['freezeRow'], $propertyValues['freezeColumn']);
+
+ return $this->generateAttributes($propertyValues);
+ }
+
+ protected function getFreezeCellPaneXml(): string
+ {
+ if ($this->freezeRow < 2 && 'A' === $this->freezeColumn) {
+ return '';
+ }
+
+ $columnIndex = CellHelper::getColumnIndexFromCellIndex($this->freezeColumn.'1');
+
+ return 'generateAttributes([
+ 'xSplit' => $columnIndex,
+ 'ySplit' => $this->freezeRow - 1,
+ 'topLeftCell' => $this->freezeColumn.$this->freezeRow,
+ 'activePane' => 'bottomRight',
+ 'state' => 'frozen',
+ ]).'/>';
+ }
+
+ /**
+ * @param array $data with key containing the attribute name and value containing the attribute value
+ */
+ protected function generateAttributes(array $data): string
+ {
+ // Create attribute for each key
+ $attributes = array_map(function ($key, $value) {
+ if (\is_bool($value)) {
+ $value = $value ? 'true' : 'false';
+ }
+
+ return $key.'="'.$value.'"';
+ }, array_keys($data), $data);
+
+ // Append all attributes
+ return ' '.implode(' ', $attributes);
+ }
+}
diff --git a/lib/spout/src/Spout/Writer/XLSX/Helper/BorderHelper.php b/lib/openspout/src/Writer/XLSX/Helper/BorderHelper.php
similarity index 55%
rename from lib/spout/src/Spout/Writer/XLSX/Helper/BorderHelper.php
rename to lib/openspout/src/Writer/XLSX/Helper/BorderHelper.php
index ed202cb63c371..1df43b9f10544 100644
--- a/lib/spout/src/Spout/Writer/XLSX/Helper/BorderHelper.php
+++ b/lib/openspout/src/Writer/XLSX/Helper/BorderHelper.php
@@ -1,50 +1,49 @@
[
- Border::WIDTH_THIN => 'thin',
+ Border::WIDTH_THIN => 'thin',
Border::WIDTH_MEDIUM => 'medium',
- Border::WIDTH_THICK => 'thick',
+ Border::WIDTH_THICK => 'thick',
],
Border::STYLE_DOTTED => [
- Border::WIDTH_THIN => 'dotted',
+ Border::WIDTH_THIN => 'dotted',
Border::WIDTH_MEDIUM => 'dotted',
- Border::WIDTH_THICK => 'dotted',
+ Border::WIDTH_THICK => 'dotted',
],
Border::STYLE_DASHED => [
- Border::WIDTH_THIN => 'dashed',
+ Border::WIDTH_THIN => 'dashed',
Border::WIDTH_MEDIUM => 'mediumDashed',
- Border::WIDTH_THICK => 'mediumDashed',
+ Border::WIDTH_THICK => 'mediumDashed',
],
Border::STYLE_DOUBLE => [
- Border::WIDTH_THIN => 'double',
+ Border::WIDTH_THIN => 'double',
Border::WIDTH_MEDIUM => 'double',
- Border::WIDTH_THICK => 'double',
+ Border::WIDTH_THICK => 'double',
],
Border::STYLE_NONE => [
- Border::WIDTH_THIN => 'none',
+ Border::WIDTH_THIN => 'none',
Border::WIDTH_MEDIUM => 'none',
- Border::WIDTH_THICK => 'none',
+ Border::WIDTH_THICK => 'none',
],
];
/**
- * @param BorderPart $borderPart
* @return string
*/
public static function serializeBorderPart(BorderPart $borderPart)
{
$borderStyle = self::getBorderStyle($borderPart);
- $colorEl = $borderPart->getColor() ? \sprintf('', $borderPart->getColor()) : '';
- $partEl = \sprintf(
+ $colorEl = $borderPart->getColor() ? sprintf('', $borderPart->getColor()) : '';
+ $partEl = sprintf(
'<%s style="%s">%s%s>',
$borderPart->getName(),
$borderStyle,
@@ -52,13 +51,12 @@ public static function serializeBorderPart(BorderPart $borderPart)
$borderPart->getName()
);
- return $partEl . PHP_EOL;
+ return $partEl.PHP_EOL;
}
/**
- * Get the style definition from the style map
+ * Get the style definition from the style map.
*
- * @param BorderPart $borderPart
* @return string
*/
protected static function getBorderStyle(BorderPart $borderPart)
diff --git a/lib/openspout/src/Writer/XLSX/Helper/DateHelper.php b/lib/openspout/src/Writer/XLSX/Helper/DateHelper.php
new file mode 100644
index 0000000000000..fb72df534d47f
--- /dev/null
+++ b/lib/openspout/src/Writer/XLSX/Helper/DateHelper.php
@@ -0,0 +1,45 @@
+format('Y');
+ $month = (int) $dateTime->format('m');
+ $day = (int) $dateTime->format('d');
+ $hours = (int) $dateTime->format('H');
+ $minutes = (int) $dateTime->format('i');
+ $seconds = (int) $dateTime->format('s');
+ // Fudge factor for the erroneous fact that the year 1900 is treated as a Leap Year in MS Excel
+ // This affects every date following 28th February 1900
+ $excel1900isLeapYear = true;
+ if ((1900 === $year) && ($month <= 2)) {
+ $excel1900isLeapYear = false;
+ }
+ $myexcelBaseDate = 2415020;
+
+ // Julian base date Adjustment
+ if ($month > 2) {
+ $month -= 3;
+ } else {
+ $month += 9;
+ --$year;
+ }
+
+ // Calculate the Julian Date, then subtract the Excel base date (JD 2415020 = 31-Dec-1899 Giving Excel Date of 0)
+ $century = (int) substr((string) $year, 0, 2);
+ $decade = (int) substr((string) $year, 2, 2);
+ $excelDate = floor((146097 * $century) / 4) + floor((1461 * $decade) / 4) + floor((153 * $month + 2) / 5) + $day + 1721119 - $myexcelBaseDate + $excel1900isLeapYear;
+
+ $excelTime = (($hours * 3600) + ($minutes * 60) + $seconds) / 86400;
+
+ return (float) $excelDate + $excelTime;
+ }
+}
diff --git a/lib/spout/src/Spout/Writer/XLSX/Helper/FileSystemHelper.php b/lib/openspout/src/Writer/XLSX/Helper/FileSystemHelper.php
similarity index 55%
rename from lib/spout/src/Spout/Writer/XLSX/Helper/FileSystemHelper.php
rename to lib/openspout/src/Writer/XLSX/Helper/FileSystemHelper.php
index 0a19d9ed84234..12e4d79c1409a 100644
--- a/lib/spout/src/Spout/Writer/XLSX/Helper/FileSystemHelper.php
+++ b/lib/openspout/src/Writer/XLSX/Helper/FileSystemHelper.php
@@ -1,38 +1,37 @@
createRootFolder()
->createRelsFolderAndFile()
->createDocPropsFolderAndFiles()
- ->createXlFolderAndSubFolders();
+ ->createXlFolderAndSubFolders()
+ ;
}
/**
- * Creates the folder that will be used as root
+ * Creates the "[Content_Types].xml" file under the root folder.
+ *
+ * @param Worksheet[] $worksheets
*
- * @throws \Box\Spout\Common\Exception\IOException If unable to create the folder
* @return FileSystemHelper
*/
- private function createRootFolder()
+ public function createContentTypesFile($worksheets)
{
- $this->rootFolder = $this->createFolder($this->baseFolderRealPath, \uniqid('xlsx', true));
+ $contentTypesXmlFileContents = <<<'EOD'
+
+
+
+
+
+ EOD;
+
+ /** @var Worksheet $worksheet */
+ foreach ($worksheets as $worksheet) {
+ $contentTypesXmlFileContents .= '';
+ }
+
+ $contentTypesXmlFileContents .= <<<'EOD'
+
+
+
+
+
+ EOD;
+
+ $this->createFileWithContents($this->rootFolder, self::CONTENT_TYPES_XML_FILE_NAME, $contentTypesXmlFileContents);
return $this;
}
/**
- * Creates the "_rels" folder under the root folder as well as the ".rels" file in it
+ * Creates the "workbook.xml" file under the "xl" folder.
+ *
+ * @param Worksheet[] $worksheets
*
- * @throws \Box\Spout\Common\Exception\IOException If unable to create the folder or the ".rels" file
* @return FileSystemHelper
*/
- private function createRelsFolderAndFile()
+ public function createWorkbookFile($worksheets)
{
- $this->relsFolder = $this->createFolder($this->rootFolder, self::RELS_FOLDER_NAME);
+ $workbookXmlFileContents = <<<'EOD'
+
+
+
+ EOD;
- $this->createRelsFile();
+ /** @var Worksheet $worksheet */
+ foreach ($worksheets as $worksheet) {
+ $worksheetName = $worksheet->getExternalSheet()->getName();
+ $worksheetVisibility = $worksheet->getExternalSheet()->isVisible() ? 'visible' : 'hidden';
+ $worksheetId = $worksheet->getId();
+ $workbookXmlFileContents .= '';
+ }
+
+ $workbookXmlFileContents .= <<<'EOD'
+
+
+ EOD;
+
+ $this->createFileWithContents($this->xlFolder, self::WORKBOOK_XML_FILE_NAME, $workbookXmlFileContents);
return $this;
}
/**
- * Creates the ".rels" file under the "_rels" folder (under root)
+ * Creates the "workbook.xml.res" file under the "xl/_res" folder.
+ *
+ * @param Worksheet[] $worksheets
*
- * @throws \Box\Spout\Common\Exception\IOException If unable to create the file
* @return FileSystemHelper
*/
- private function createRelsFile()
+ public function createWorkbookRelsFile($worksheets)
{
- $relsFileContents = <<<'EOD'
-
-
-
-
-
-
-EOD;
+ $workbookRelsXmlFileContents = <<<'EOD'
+
+
+
+
+ EOD;
- $this->createFileWithContents($this->relsFolder, self::RELS_FILE_NAME, $relsFileContents);
+ /** @var Worksheet $worksheet */
+ foreach ($worksheets as $worksheet) {
+ $worksheetId = $worksheet->getId();
+ $workbookRelsXmlFileContents .= '';
+ }
+
+ $workbookRelsXmlFileContents .= '';
+
+ $this->createFileWithContents($this->xlRelsFolder, self::WORKBOOK_RELS_XML_FILE_NAME, $workbookRelsXmlFileContents);
return $this;
}
/**
- * Creates the "docProps" folder under the root folder as well as the "app.xml" and "core.xml" files in it
+ * Creates the "styles.xml" file under the "xl" folder.
+ *
+ * @param StyleManager $styleManager
*
- * @throws \Box\Spout\Common\Exception\IOException If unable to create the folder or one of the files
* @return FileSystemHelper
*/
- private function createDocPropsFolderAndFiles()
+ public function createStylesFile($styleManager)
{
- $this->docPropsFolder = $this->createFolder($this->rootFolder, self::DOC_PROPS_FOLDER_NAME);
-
- $this->createAppXmlFile();
- $this->createCoreXmlFile();
+ $stylesXmlFileContents = $styleManager->getStylesXMLFileContent();
+ $this->createFileWithContents($this->xlFolder, self::STYLES_XML_FILE_NAME, $stylesXmlFileContents);
return $this;
}
/**
- * Creates the "app.xml" file under the "docProps" folder
+ * Zips the root folder and streams the contents of the zip into the given stream.
*
- * @throws \Box\Spout\Common\Exception\IOException If unable to create the file
- * @return FileSystemHelper
+ * @param resource $streamPointer Pointer to the stream to copy the zip
*/
- private function createAppXmlFile()
+ public function zipRootFolderAndCopyToStream($streamPointer)
{
- $appName = self::APP_NAME;
- $appXmlFileContents = <<
-
- $appName
- 0
-
-EOD;
+ $zip = $this->zipHelper->createZip($this->rootFolder);
- $this->createFileWithContents($this->docPropsFolder, self::APP_XML_FILE_NAME, $appXmlFileContents);
+ $zipFilePath = $this->zipHelper->getZipFilePath($zip);
- return $this;
+ // 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 "core.xml" file under the "docProps" folder
+ * Creates the folder that will be used as root.
+ *
+ * @throws \OpenSpout\Common\Exception\IOException If unable to create the folder
*
- * @throws \Box\Spout\Common\Exception\IOException If unable to create the file
* @return FileSystemHelper
*/
- private function createCoreXmlFile()
+ private function createRootFolder()
{
- $createdDate = (new \DateTime())->format(\DateTime::W3C);
- $coreXmlFileContents = <<
-
- $createdDate
- $createdDate
- 0
-
-EOD;
-
- $this->createFileWithContents($this->docPropsFolder, self::CORE_XML_FILE_NAME, $coreXmlFileContents);
+ $this->rootFolder = $this->createFolder($this->baseFolderRealPath, uniqid('xlsx', true));
return $this;
}
/**
- * Creates the "xl" folder under the root folder as well as its subfolders
+ * 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
*
- * @throws \Box\Spout\Common\Exception\IOException If unable to create at least one of the folders
* @return FileSystemHelper
*/
- private function createXlFolderAndSubFolders()
+ private function createRelsFolderAndFile()
{
- $this->xlFolder = $this->createFolder($this->rootFolder, self::XL_FOLDER_NAME);
- $this->createXlRelsFolder();
- $this->createXlWorksheetsFolder();
+ $this->relsFolder = $this->createFolder($this->rootFolder, self::RELS_FOLDER_NAME);
+
+ $this->createRelsFile();
return $this;
}
/**
- * Creates the "_rels" folder under the "xl" folder
+ * Creates the ".rels" file under the "_rels" folder (under root).
+ *
+ * @throws \OpenSpout\Common\Exception\IOException If unable to create the file
*
- * @throws \Box\Spout\Common\Exception\IOException If unable to create the folder
* @return FileSystemHelper
*/
- private function createXlRelsFolder()
+ private function createRelsFile()
{
- $this->xlRelsFolder = $this->createFolder($this->xlFolder, self::RELS_FOLDER_NAME);
+ $relsFileContents = <<<'EOD'
+
+
+
+
+
+
+ EOD;
+
+ $this->createFileWithContents($this->relsFolder, self::RELS_FILE_NAME, $relsFileContents);
return $this;
}
/**
- * Creates the "worksheets" folder under the "xl" folder
+ * 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
*
- * @throws \Box\Spout\Common\Exception\IOException If unable to create the folder
* @return FileSystemHelper
*/
- private function createXlWorksheetsFolder()
+ private function createDocPropsFolderAndFiles()
{
- $this->xlWorksheetsFolder = $this->createFolder($this->xlFolder, self::WORKSHEETS_FOLDER_NAME);
+ $this->docPropsFolder = $this->createFolder($this->rootFolder, self::DOC_PROPS_FOLDER_NAME);
+
+ $this->createAppXmlFile();
+ $this->createCoreXmlFile();
return $this;
}
/**
- * Creates the "[Content_Types].xml" file under the root folder
+ * Creates the "app.xml" file under the "docProps" folder.
+ *
+ * @throws \OpenSpout\Common\Exception\IOException If unable to create the file
*
- * @param Worksheet[] $worksheets
* @return FileSystemHelper
*/
- public function createContentTypesFile($worksheets)
+ private function createAppXmlFile()
{
- $contentTypesXmlFileContents = <<<'EOD'
-
-
-
-
-
-EOD;
-
- /** @var Worksheet $worksheet */
- foreach ($worksheets as $worksheet) {
- $contentTypesXmlFileContents .= '';
- }
-
- $contentTypesXmlFileContents .= <<<'EOD'
-
-
-
-
-
-EOD;
+ $appName = self::APP_NAME;
+ $appXmlFileContents = <<
+
+ {$appName}
+ 0
+
+ EOD;
- $this->createFileWithContents($this->rootFolder, self::CONTENT_TYPES_XML_FILE_NAME, $contentTypesXmlFileContents);
+ $this->createFileWithContents($this->docPropsFolder, self::APP_XML_FILE_NAME, $appXmlFileContents);
return $this;
}
/**
- * Creates the "workbook.xml" file under the "xl" folder
+ * Creates the "core.xml" file under the "docProps" folder.
+ *
+ * @throws \OpenSpout\Common\Exception\IOException If unable to create the file
*
- * @param Worksheet[] $worksheets
* @return FileSystemHelper
*/
- public function createWorkbookFile($worksheets)
+ private function createCoreXmlFile()
{
- $workbookXmlFileContents = <<<'EOD'
-
-
-
-EOD;
-
- /** @var Worksheet $worksheet */
- foreach ($worksheets as $worksheet) {
- $worksheetName = $worksheet->getExternalSheet()->getName();
- $worksheetVisibility = $worksheet->getExternalSheet()->isVisible() ? 'visible' : 'hidden';
- $worksheetId = $worksheet->getId();
- $workbookXmlFileContents .= '';
- }
-
- $workbookXmlFileContents .= <<<'EOD'
-
-
-EOD;
+ $createdDate = (new \DateTime())->format(\DateTime::W3C);
+ $coreXmlFileContents = <<
+
+ {$createdDate}
+ {$createdDate}
+ 0
+
+ EOD;
- $this->createFileWithContents($this->xlFolder, self::WORKBOOK_XML_FILE_NAME, $workbookXmlFileContents);
+ $this->createFileWithContents($this->docPropsFolder, self::CORE_XML_FILE_NAME, $coreXmlFileContents);
return $this;
}
/**
- * Creates the "workbook.xml.res" file under the "xl/_res" folder
+ * 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
*
- * @param Worksheet[] $worksheets
* @return FileSystemHelper
*/
- public function createWorkbookRelsFile($worksheets)
+ private function createXlFolderAndSubFolders()
{
- $workbookRelsXmlFileContents = <<<'EOD'
-
-
-
-
-EOD;
-
- /** @var Worksheet $worksheet */
- foreach ($worksheets as $worksheet) {
- $worksheetId = $worksheet->getId();
- $workbookRelsXmlFileContents .= '';
- }
-
- $workbookRelsXmlFileContents .= '';
-
- $this->createFileWithContents($this->xlRelsFolder, self::WORKBOOK_RELS_XML_FILE_NAME, $workbookRelsXmlFileContents);
+ $this->xlFolder = $this->createFolder($this->rootFolder, self::XL_FOLDER_NAME);
+ $this->createXlRelsFolder();
+ $this->createXlWorksheetsFolder();
return $this;
}
/**
- * Creates the "styles.xml" file under the "xl" folder
+ * Creates the "_rels" folder under the "xl" folder.
+ *
+ * @throws \OpenSpout\Common\Exception\IOException If unable to create the folder
*
- * @param StyleManager $styleManager
* @return FileSystemHelper
*/
- public function createStylesFile($styleManager)
+ private function createXlRelsFolder()
{
- $stylesXmlFileContents = $styleManager->getStylesXMLFileContent();
- $this->createFileWithContents($this->xlFolder, self::STYLES_XML_FILE_NAME, $stylesXmlFileContents);
+ $this->xlRelsFolder = $this->createFolder($this->xlFolder, self::RELS_FOLDER_NAME);
return $this;
}
/**
- * Zips the root folder and streams the contents of the zip into the given stream
+ * Creates the "worksheets" folder under the "xl" folder.
*
- * @param resource $streamPointer Pointer to the stream to copy the zip
- * @return void
+ * @throws \OpenSpout\Common\Exception\IOException If unable to create the folder
+ *
+ * @return FileSystemHelper
*/
- public function zipRootFolderAndCopyToStream($streamPointer)
+ private function createXlWorksheetsFolder()
{
- $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);
+ $this->xlWorksheetsFolder = $this->createFolder($this->xlFolder, self::WORKSHEETS_FOLDER_NAME);
- // once the zip is copied, remove it
- $this->deleteFile($zipFilePath);
+ return $this;
}
}
diff --git a/lib/spout/src/Spout/Writer/XLSX/Manager/OptionsManager.php b/lib/openspout/src/Writer/XLSX/Manager/OptionsManager.php
similarity index 64%
rename from lib/spout/src/Spout/Writer/XLSX/Manager/OptionsManager.php
rename to lib/openspout/src/Writer/XLSX/Manager/OptionsManager.php
index d3b5cd423561b..b7e7eaebd1f5c 100644
--- a/lib/spout/src/Spout/Writer/XLSX/Manager/OptionsManager.php
+++ b/lib/openspout/src/Writer/XLSX/Manager/OptionsManager.php
@@ -1,27 +1,25 @@
styleBuilder
->setFontSize(self::DEFAULT_FONT_SIZE)
->setFontName(self::DEFAULT_FONT_NAME)
- ->build();
+ ->build()
+ ;
- $this->setOption(Options::TEMP_FOLDER, \sys_get_temp_dir());
+ $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, []);
}
}
diff --git a/lib/spout/src/Spout/Writer/XLSX/Manager/SharedStringsManager.php b/lib/openspout/src/Writer/XLSX/Manager/SharedStringsManager.php
similarity index 58%
rename from lib/spout/src/Spout/Writer/XLSX/Manager/SharedStringsManager.php
rename to lib/openspout/src/Writer/XLSX/Manager/SharedStringsManager.php
index b0a5b48d6c9c2..97395561a22d7 100644
--- a/lib/spout/src/Spout/Writer/XLSX/Manager/SharedStringsManager.php
+++ b/lib/openspout/src/Writer/XLSX/Manager/SharedStringsManager.php
@@ -1,28 +1,27 @@
-
+ sharedStringsFilePointer = \fopen($sharedStringsFilePath, 'w');
+ $sharedStringsFilePath = $xlFolder.'/'.self::SHARED_STRINGS_FILE_NAME;
+ $this->sharedStringsFilePointer = fopen($sharedStringsFilePath, 'w');
$this->throwIfSharedStringsFilePointerIsNotAvailable();
// the headers is split into different parts so that we can fseek and put in the correct count and uniqueCount later
- $header = self::SHARED_STRINGS_XML_FILE_FIRST_PART_HEADER . ' ' . self::DEFAULT_STRINGS_COUNT_PART . '>';
- \fwrite($this->sharedStringsFilePointer, $header);
+ $header = self::SHARED_STRINGS_XML_FILE_FIRST_PART_HEADER.' '.self::DEFAULT_STRINGS_COUNT_PART.'>';
+ fwrite($this->sharedStringsFilePointer, $header);
$this->stringsEscaper = $stringsEscaper;
}
- /**
- * Checks if the book has been created. Throws an exception if not created yet.
- *
- * @throws \Box\Spout\Common\Exception\IOException If the sheet data file cannot be opened for writing
- * @return void
- */
- protected function throwIfSharedStringsFilePointerIsNotAvailable()
- {
- if (!$this->sharedStringsFilePointer) {
- throw new IOException('Unable to open shared strings file for writing.');
- }
- }
-
/**
* Writes the given string into the sharedStrings.xml file.
* Starting and ending whitespaces are preserved.
*
* @param string $string
+ *
* @return int ID of the written shared string
*/
public function writeString($string)
{
- \fwrite($this->sharedStringsFilePointer, '' . $this->stringsEscaper->escape($string) . '');
- $this->numSharedStrings++;
+ fwrite($this->sharedStringsFilePointer, ''.$this->stringsEscaper->escape($string).'');
+ ++$this->numSharedStrings;
// Shared string ID is zero-based
- return ($this->numSharedStrings - 1);
+ return $this->numSharedStrings - 1;
}
/**
* Finishes writing the data in the sharedStrings.xml file and closes the file.
- *
- * @return void
*/
public function close()
{
@@ -91,16 +76,28 @@ public function close()
return;
}
- \fwrite($this->sharedStringsFilePointer, '');
+ fwrite($this->sharedStringsFilePointer, '');
// Replace the default strings count with the actual number of shared strings in the file header
$firstPartHeaderLength = \strlen(self::SHARED_STRINGS_XML_FILE_FIRST_PART_HEADER);
$defaultStringsCountPartLength = \strlen(self::DEFAULT_STRINGS_COUNT_PART);
// Adding 1 to take into account the space between the last xml attribute and "count"
- \fseek($this->sharedStringsFilePointer, $firstPartHeaderLength + 1);
- \fwrite($this->sharedStringsFilePointer, \sprintf("%-{$defaultStringsCountPartLength}s", 'count="' . $this->numSharedStrings . '" uniqueCount="' . $this->numSharedStrings . '"'));
+ fseek($this->sharedStringsFilePointer, $firstPartHeaderLength + 1);
+ fwrite($this->sharedStringsFilePointer, sprintf("%-{$defaultStringsCountPartLength}s", 'count="'.$this->numSharedStrings.'" uniqueCount="'.$this->numSharedStrings.'"'));
- \fclose($this->sharedStringsFilePointer);
+ fclose($this->sharedStringsFilePointer);
+ }
+
+ /**
+ * Checks if the book has been created. Throws an exception if not created yet.
+ *
+ * @throws \OpenSpout\Common\Exception\IOException If the sheet data file cannot be opened for writing
+ */
+ protected function throwIfSharedStringsFilePointerIsNotAvailable()
+ {
+ if (!\is_resource($this->sharedStringsFilePointer)) {
+ throw new IOException('Unable to open shared strings file for writing.');
+ }
}
}
diff --git a/lib/spout/src/Spout/Writer/XLSX/Manager/Style/StyleManager.php b/lib/openspout/src/Writer/XLSX/Manager/Style/StyleManager.php
similarity index 75%
rename from lib/spout/src/Spout/Writer/XLSX/Manager/Style/StyleManager.php
rename to lib/openspout/src/Writer/XLSX/Manager/Style/StyleManager.php
index f0ca9d9e61c4d..5f284b9511d2d 100644
--- a/lib/spout/src/Spout/Writer/XLSX/Manager/Style/StyleManager.php
+++ b/lib/openspout/src/Writer/XLSX/Manager/Style/StyleManager.php
@@ -1,16 +1,16 @@
styleRegistry->getFillIdForStyleId($styleId);
- $hasStyleCustomFill = ($associatedFillId !== null && $associatedFillId !== 0);
+ $hasStyleCustomFill = (null !== $associatedFillId && 0 !== $associatedFillId);
$associatedBorderId = $this->styleRegistry->getBorderIdForStyleId($styleId);
- $hasStyleCustomBorders = ($associatedBorderId !== null && $associatedBorderId !== 0);
+ $hasStyleCustomBorders = (null !== $associatedBorderId && 0 !== $associatedBorderId);
$associatedFormatId = $this->styleRegistry->getFormatIdForStyleId($styleId);
- $hasStyleCustomFormats = ($associatedFormatId !== null && $associatedFormatId !== 0);
+ $hasStyleCustomFormats = (null !== $associatedFormatId && 0 !== $associatedFormatId);
- return ($hasStyleCustomFill || $hasStyleCustomBorders || $hasStyleCustomFormats);
+ return $hasStyleCustomFill || $hasStyleCustomBorders || $hasStyleCustomFormats;
}
/**
@@ -47,9 +48,9 @@ public function shouldApplyStyleOnEmptyCell($styleId)
public function getStylesXMLFileContent()
{
$content = <<<'EOD'
-
-
-EOD;
+
+
+ EOD;
$content .= $this->getFormatsSectionContent();
$content .= $this->getFontsSectionContent();
@@ -60,8 +61,8 @@ public function getStylesXMLFileContent()
$content .= $this->getCellStylesSectionContent();
$content .= <<<'EOD'
-
-EOD;
+
+ EOD;
return $content;
}
@@ -86,10 +87,10 @@ protected function getFormatsSectionContent()
/** @var Style $style */
$style = $this->styleRegistry->getStyleFromStyleId($styleId);
$format = $style->getFormat();
- $tags[] = '';
+ $tags[] = '';
}
- $content = '';
- $content .= \implode('', $tags);
+ $content = '';
+ $content .= implode('', $tags);
$content .= '';
return $content;
@@ -104,15 +105,15 @@ protected function getFontsSectionContent()
{
$registeredStyles = $this->styleRegistry->getRegisteredStyles();
- $content = '';
+ $content = '';
/** @var Style $style */
foreach ($registeredStyles as $style) {
$content .= '';
- $content .= '';
- $content .= '';
- $content .= '';
+ $content .= '';
+ $content .= '';
+ $content .= '';
if ($style->isFontBold()) {
$content .= '';
@@ -146,7 +147,7 @@ protected function getFillsSectionContent()
// Excel reserves two default fills
$fillsCount = \count($registeredFills) + 2;
- $content = \sprintf('', $fillsCount);
+ $content = sprintf('', $fillsCount);
$content .= '';
$content .= '';
@@ -157,7 +158,7 @@ protected function getFillsSectionContent()
$style = $this->styleRegistry->getStyleFromStyleId($styleId);
$backgroundColor = $style->getBackgroundColor();
- $content .= \sprintf(
+ $content .= sprintf(
'',
$backgroundColor
);
@@ -180,23 +181,23 @@ protected function getBordersSectionContent()
// There is one default border with index 0
$borderCount = \count($registeredBorders) + 1;
- $content = '';
+ $content = '';
// Default border starting at index 0
$content .= '';
foreach ($registeredBorders as $styleId) {
- /** @var \Box\Spout\Common\Entity\Style\Style $style */
+ /** @var Style $style */
$style = $this->styleRegistry->getStyleFromStyleId($styleId);
$border = $style->getBorder();
$content .= '';
- // @link https://github.com/box/spout/issues/271
+ /** @see https://github.com/box/spout/issues/271 */
$sortOrder = ['left', 'right', 'top', 'bottom'];
foreach ($sortOrder as $partName) {
if ($border->hasPart($partName)) {
- /** @var $part \Box\Spout\Common\Entity\Style\BorderPart */
+ /** @var BorderPart $part */
$part = $border->getPart($partName);
$content .= BorderHelper::serializeBorderPart($part);
}
@@ -218,10 +219,10 @@ protected function getBordersSectionContent()
protected function getCellStyleXfsSectionContent()
{
return <<<'EOD'
-
-
-
-EOD;
+
+
+
+ EOD;
}
/**
@@ -233,7 +234,7 @@ protected function getCellXfsSectionContent()
{
$registeredStyles = $this->styleRegistry->getRegisteredStyles();
- $content = '';
+ $content = '';
foreach ($registeredStyles as $style) {
$styleId = $style->getId();
@@ -241,23 +242,27 @@ protected function getCellXfsSectionContent()
$borderId = $this->getBorderIdForStyleId($styleId);
$numFmtId = $this->getFormatIdForStyleId($styleId);
- $content .= 'shouldApplyFont()) {
$content .= ' applyFont="1"';
}
- $content .= \sprintf(' applyBorder="%d"', $style->shouldApplyBorder() ? 1 : 0);
+ $content .= sprintf(' applyBorder="%d"', $style->shouldApplyBorder() ? 1 : 0);
- if ($style->shouldApplyCellAlignment() || $style->shouldWrapText()) {
+ if ($style->shouldApplyCellAlignment() || $style->shouldWrapText() || $style->shouldShrinkToFit()) {
$content .= ' applyAlignment="1">';
$content .= 'shouldApplyCellAlignment()) {
- $content .= \sprintf(' horizontal="%s"', $style->getCellAlignment());
+ $content .= sprintf(' horizontal="%s"', $style->getCellAlignment());
}
if ($style->shouldWrapText()) {
$content .= ' wrapText="1"';
}
+ if ($style->shouldShrinkToFit()) {
+ $content .= ' shrinkToFit="true"';
+ }
+
$content .= '/>';
$content .= '';
} else {
@@ -270,18 +275,33 @@ protected function getCellXfsSectionContent()
return $content;
}
+ /**
+ * Returns the content of the "" section.
+ *
+ * @return string
+ */
+ protected function getCellStylesSectionContent()
+ {
+ return <<<'EOD'
+
+
+
+ EOD;
+ }
+
/**
* Returns the fill ID associated to the given style ID.
* For the default style, we don't a fill.
*
* @param int $styleId
+ *
* @return int
*/
private function getFillIdForStyleId($styleId)
{
// For the default style (ID = 0), we don't want to override the fill.
// Otherwise all cells of the spreadsheet will have a background color.
- $isDefaultStyle = ($styleId === 0);
+ $isDefaultStyle = (0 === $styleId);
return $isDefaultStyle ? 0 : ($this->styleRegistry->getFillIdForStyleId($styleId) ?: 0);
}
@@ -291,13 +311,14 @@ private function getFillIdForStyleId($styleId)
* For the default style, we don't a border.
*
* @param int $styleId
+ *
* @return int
*/
private function getBorderIdForStyleId($styleId)
{
// For the default style (ID = 0), we don't want to override the border.
// Otherwise all cells of the spreadsheet will have a border.
- $isDefaultStyle = ($styleId === 0);
+ $isDefaultStyle = (0 === $styleId);
return $isDefaultStyle ? 0 : ($this->styleRegistry->getBorderIdForStyleId($styleId) ?: 0);
}
@@ -307,28 +328,15 @@ private function getBorderIdForStyleId($styleId)
* For the default style use general format.
*
* @param int $styleId
+ *
* @return int
*/
private function getFormatIdForStyleId($styleId)
{
// For the default style (ID = 0), we don't want to override the format.
// Otherwise all cells of the spreadsheet will have a format.
- $isDefaultStyle = ($styleId === 0);
+ $isDefaultStyle = (0 === $styleId);
return $isDefaultStyle ? 0 : ($this->styleRegistry->getFormatIdForStyleId($styleId) ?: 0);
}
-
- /**
- * Returns the content of the "" section.
- *
- * @return string
- */
- protected function getCellStylesSectionContent()
- {
- return <<<'EOD'
-
-
-
-EOD;
- }
}
diff --git a/lib/spout/src/Spout/Writer/XLSX/Manager/Style/StyleRegistry.php b/lib/openspout/src/Writer/XLSX/Manager/Style/StyleRegistry.php
similarity index 88%
rename from lib/spout/src/Spout/Writer/XLSX/Manager/Style/StyleRegistry.php
rename to lib/openspout/src/Writer/XLSX/Manager/Style/StyleRegistry.php
index 14eb9862361f2..259d4e8408d00 100644
--- a/lib/spout/src/Spout/Writer/XLSX/Manager/Style/StyleRegistry.php
+++ b/lib/openspout/src/Writer/XLSX/Manager/Style/StyleRegistry.php
@@ -1,17 +1,17 @@
styleIdToFormatsMappingTable[$styleId] ?? null;
+ }
+
+ /**
+ * @param int $styleId
+ *
+ * @return null|int Fill ID associated to the given style ID
+ */
+ public function getFillIdForStyleId($styleId)
+ {
+ return (isset($this->styleIdToFillMappingTable[$styleId])) ?
+ $this->styleIdToFillMappingTable[$styleId] :
+ null;
+ }
+
+ /**
+ * @param int $styleId
*
- * @param Style $style
+ * @return null|int Fill ID associated to the given style ID
+ */
+ public function getBorderIdForStyleId($styleId)
+ {
+ return (isset($this->styleIdToBorderMappingTable[$styleId])) ?
+ $this->styleIdToBorderMappingTable[$styleId] :
+ null;
+ }
+
+ /**
+ * @return array
+ */
+ public function getRegisteredFills()
+ {
+ return $this->registeredFills;
+ }
+
+ /**
+ * @return array
+ */
+ public function getRegisteredBorders()
+ {
+ return $this->registeredBorders;
+ }
+
+ /**
+ * @return array
+ */
+ public function getRegisteredFormats()
+ {
+ return $this->registeredFormats;
+ }
+
+ /**
+ * Register a format definition.
*/
protected function registerFormat(Style $style)
{
@@ -163,18 +218,7 @@ protected function registerFormat(Style $style)
}
/**
- * @param int $styleId
- * @return int|null Format ID associated to the given style ID
- */
- public function getFormatIdForStyleId($styleId)
- {
- return $this->styleIdToFormatsMappingTable[$styleId] ?? null;
- }
-
- /**
- * Register a fill definition
- *
- * @param Style $style
+ * Register a fill definition.
*/
private function registerFill(Style $style)
{
@@ -204,20 +248,7 @@ private function registerFill(Style $style)
}
/**
- * @param int $styleId
- * @return int|null Fill ID associated to the given style ID
- */
- public function getFillIdForStyleId($styleId)
- {
- return (isset($this->styleIdToFillMappingTable[$styleId])) ?
- $this->styleIdToFillMappingTable[$styleId] :
- null;
- }
-
- /**
- * Register a border definition
- *
- * @param Style $style
+ * Register a border definition.
*/
private function registerBorder(Style $style)
{
@@ -225,7 +256,7 @@ private function registerBorder(Style $style)
if ($style->shouldApplyBorder()) {
$border = $style->getBorder();
- $serializedBorder = \serialize($border);
+ $serializedBorder = serialize($border);
$isBorderAlreadyRegistered = isset($this->registeredBorders[$serializedBorder]);
@@ -242,39 +273,4 @@ private function registerBorder(Style $style)
$this->styleIdToBorderMappingTable[$styleId] = 0;
}
}
-
- /**
- * @param int $styleId
- * @return int|null Fill ID associated to the given style ID
- */
- public function getBorderIdForStyleId($styleId)
- {
- return (isset($this->styleIdToBorderMappingTable[$styleId])) ?
- $this->styleIdToBorderMappingTable[$styleId] :
- null;
- }
-
- /**
- * @return array
- */
- public function getRegisteredFills()
- {
- return $this->registeredFills;
- }
-
- /**
- * @return array
- */
- public function getRegisteredBorders()
- {
- return $this->registeredBorders;
- }
-
- /**
- * @return array
- */
- public function getRegisteredFormats()
- {
- return $this->registeredFormats;
- }
}
diff --git a/lib/spout/src/Spout/Writer/XLSX/Manager/WorkbookManager.php b/lib/openspout/src/Writer/XLSX/Manager/WorkbookManager.php
similarity index 74%
rename from lib/spout/src/Spout/Writer/XLSX/Manager/WorkbookManager.php
rename to lib/openspout/src/Writer/XLSX/Manager/WorkbookManager.php
index 708208bd51599..2feb331d86c9c 100644
--- a/lib/spout/src/Spout/Writer/XLSX/Manager/WorkbookManager.php
+++ b/lib/openspout/src/Writer/XLSX/Manager/WorkbookManager.php
@@ -1,20 +1,20 @@
fileSystemHelper->getXlWorksheetsFolder();
+
+ return $worksheetFilesFolder.'/'.strtolower($sheet->getName()).'.xml';
}
/**
- * @param Sheet $sheet
- * @return string The file path where the data for the given sheet will be stored
+ * @return int Maximum number of rows/columns a sheet can contain
*/
- public function getWorksheetFilePath(Sheet $sheet)
+ protected function getMaxRowsPerWorksheet()
{
- $worksheetFilesFolder = $this->fileSystemHelper->getXlWorksheetsFolder();
-
- return $worksheetFilesFolder . '/' . \strtolower($sheet->getName()) . '.xml';
+ return self::$maxRowsPerWorksheet;
}
/**
- * Closes custom objects that are still opened
- *
- * @return void
+ * Closes custom objects that are still opened.
*/
protected function closeRemainingObjects()
{
@@ -61,7 +58,6 @@ protected function closeRemainingObjects()
* 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
- * @return void
*/
protected function writeAllFilesToDiskAndZipThem($finalFilePointer)
{
@@ -72,6 +68,7 @@ protected function writeAllFilesToDiskAndZipThem($finalFilePointer)
->createWorkbookFile($worksheets)
->createWorkbookRelsFile($worksheets)
->createStylesFile($this->styleManager)
- ->zipRootFolderAndCopyToStream($finalFilePointer);
+ ->zipRootFolderAndCopyToStream($finalFilePointer)
+ ;
}
}
diff --git a/lib/spout/src/Spout/Writer/XLSX/Manager/WorksheetManager.php b/lib/openspout/src/Writer/XLSX/Manager/WorksheetManager.php
similarity index 53%
rename from lib/spout/src/Spout/Writer/XLSX/Manager/WorksheetManager.php
rename to lib/openspout/src/Writer/XLSX/Manager/WorksheetManager.php
index 61b93a17619ec..c84be5ab55570 100644
--- a/lib/spout/src/Spout/Writer/XLSX/Manager/WorksheetManager.php
+++ b/lib/openspout/src/Writer/XLSX/Manager/WorksheetManager.php
@@ -1,47 +1,53 @@
-
-EOD;
+ public const SHEET_XML_FILE_HEADER = <<<'EOD'
+
+
+ EOD;
/** @var bool Whether inline or shared strings should be used */
protected $shouldUseInlineStrings;
+ /** @var OptionsManagerInterface */
+ private $optionsManager;
+
/** @var RowManager Manages rows */
private $rowManager;
@@ -60,20 +66,8 @@ class WorksheetManager implements WorksheetManagerInterface
/** @var StringHelper String helper */
private $stringHelper;
- /** @var InternalEntityFactory Factory to create entities */
- private $entityFactory;
-
/**
* WorksheetManager constructor.
- *
- * @param OptionsManagerInterface $optionsManager
- * @param RowManager $rowManager
- * @param StyleManager $styleManager
- * @param StyleMerger $styleMerger
- * @param SharedStringsManager $sharedStringsManager
- * @param XLSXEscaper $stringsEscaper
- * @param StringHelper $stringHelper
- * @param InternalEntityFactory $entityFactory
*/
public function __construct(
OptionsManagerInterface $optionsManager,
@@ -82,17 +76,19 @@ public function __construct(
StyleMerger $styleMerger,
SharedStringsManager $sharedStringsManager,
XLSXEscaper $stringsEscaper,
- StringHelper $stringHelper,
- InternalEntityFactory $entityFactory
+ 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;
- $this->entityFactory = $entityFactory;
}
/**
@@ -108,57 +104,146 @@ public function getSharedStringsManager()
*/
public function startSheet(Worksheet $worksheet)
{
- $sheetFilePointer = \fopen($worksheet->getFilePath(), 'w');
+ $sheetFilePointer = fopen($worksheet->getFilePath(), 'w');
$this->throwIfSheetFilePointerIsNotAvailable($sheetFilePointer);
$worksheet->setFilePointer($sheetFilePointer);
- \fwrite($sheetFilePointer, self::SHEET_XML_FILE_HEADER);
- \fwrite($sheetFilePointer, '');
+ fwrite($sheetFilePointer, self::SHEET_XML_FILE_HEADER);
}
/**
- * Checks if the sheet has been sucessfully created. Throws an exception if not.
+ * {@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.
*
- * @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
- * @return void
+ * @return string
*/
- private function throwIfSheetFilePointerIsNotAvailable($sheetFilePointer)
+ public function getXMLFragmentForColumnWidths()
{
- if (!$sheetFilePointer) {
- throw new IOException('Unable to open sheet for writing.');
+ if (empty($this->columnWidths)) {
+ return '';
+ }
+ $xml = '';
+ foreach ($this->columnWidths as $entry) {
+ $xml .= '';
+ }
+ $xml .= '';
+
+ 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 "";
}
/**
* {@inheritdoc}
*/
- public function addRow(Worksheet $worksheet, Row $row)
+ public function close(Worksheet $worksheet)
{
- if (!$this->rowManager->isEmpty($row)) {
- $this->addNonEmptyRow($worksheet, $row);
+ $worksheetFilePointer = $worksheet->getFilePointer();
+
+ if (!\is_resource($worksheetFilePointer)) {
+ return;
+ }
+ $this->ensureSheetDataStated($worksheet);
+ fwrite($worksheetFilePointer, '');
+
+ // create nodes for merge cells
+ if ($this->optionsManager->getOption(Options::MERGE_CELLS)) {
+ $mergeCellString = '';
+ foreach ($this->optionsManager->getOption(Options::MERGE_CELLS) as $values) {
+ $output = array_map(function ($value) {
+ return CellHelper::getColumnLettersFromColumnIndex($value[0]).$value[1];
+ }, $values);
+ $mergeCellString .= '';
+ }
+ $mergeCellString .= '';
+ fwrite($worksheet->getFilePointer(), $mergeCellString);
}
- $worksheet->setLastWrittenRowIndex($worksheet->getLastWrittenRowIndex() + 1);
+ fwrite($worksheetFilePointer, '');
+ 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, ''.$sheet->getSheetView()->getXml().'');
+ }
+ fwrite($worksheetFilePointer, $this->getXMLFragmentForDefaultCellSizing());
+ fwrite($worksheetFilePointer, $this->getXMLFragmentForColumnWidths());
+ fwrite($worksheetFilePointer, '');
+ $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 IOException If the data cannot be written
+ * @param Row $row The row to be written
+ *
* @throws InvalidArgumentException If a cell value's type is not supported
- * @return void
+ * @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();
- $rowXML = '';
+ $hasCustomHeight = $this->defaultRowHeight > 0 ? '1' : '0';
+ $rowXML = "";
foreach ($row->getCells() as $columnIndexZeroBased => $cell) {
$registeredStyle = $this->applyStyleAndRegister($cell, $rowStyle);
@@ -171,22 +256,18 @@ private function addNonEmptyRow(Worksheet $worksheet, Row $row)
$rowXML .= ' ';
- $wasWriteSuccessful = \fwrite($worksheet->getFilePointer(), $rowXML);
- if ($wasWriteSuccessful === false) {
+ $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
- *
- * @param Cell $cell
- * @param Style $rowStyle
+ * 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
- * @return RegisteredStyle
*/
- private function applyStyleAndRegister(Cell $cell, Style $rowStyle) : RegisteredStyle
+ private function applyStyleAndRegister(Cell $cell, Style $rowStyle): RegisteredStyle
{
$isMatchingRowStyle = false;
if ($cell->getStyle()->isEmpty()) {
@@ -221,29 +302,38 @@ private function applyStyleAndRegister(Cell $cell, Style $rowStyle) : Registered
/**
* Builds and returns xml for a single cell.
*
- * @param int $rowIndexOneBased
- * @param int $columnIndexZeroBased
- * @param Cell $cell
- * @param int $styleId
+ * @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 = 'isString()) {
$cellXML .= $this->getCellXMLFragmentForNonEmptyString($cell->getValue());
} elseif ($cell->isBoolean()) {
- $cellXML .= ' t="b">' . (int) ($cell->getValue()) . '';
+ $cellXML .= ' t="b">'.(int) ($cell->getValue()).'';
} elseif ($cell->isNumeric()) {
- $cellXML .= '>' . $this->stringHelper->formatNumericValue($cell->getValue()) . '';
- } elseif ($cell->isError() && is_string($cell->getValueEvenIfError())) {
+ $cellXML .= '>'.$this->stringHelper->formatNumericValue($cell->getValue()).'';
+ } elseif ($cell->isFormula()) {
+ $cellXML .= '>'.substr($cell->getValue(), 1).'';
+ } elseif ($cell->isDate()) {
+ $value = $cell->getValue();
+ if ($value instanceof \DateTimeInterface) {
+ $cellXML .= '>'.(string) DateHelper::toExcel($value).'';
+ } 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">' . $cell->getValueEvenIfError() . '';
+ $cellXML .= ' t="e">'.$cell->getValueEvenIfError().'';
} elseif ($cell->isEmpty()) {
if ($this->styleManager->shouldApplyStyleOnEmptyCell($styleId)) {
$cellXML .= '/>';
@@ -253,17 +343,19 @@ private function getCellXML($rowIndexOneBased, $columnIndexZeroBased, Cell $cell
$cellXML = '';
}
} else {
- throw new InvalidArgumentException('Trying to add a value with an unsupported type: ' . \gettype($cell->getValue()));
+ 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
+ * 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)
@@ -273,28 +365,12 @@ private function getCellXMLFragmentForNonEmptyString($cellValue)
}
if ($this->shouldUseInlineStrings) {
- $cellXMLFragment = ' t="inlineStr">' . $this->stringsEscaper->escape($cellValue) . '';
+ $cellXMLFragment = ' t="inlineStr">'.$this->stringsEscaper->escape($cellValue).'';
} else {
$sharedStringId = $this->sharedStringsManager->writeString($cellValue);
- $cellXMLFragment = ' t="s">' . $sharedStringId . '';
+ $cellXMLFragment = ' t="s">'.$sharedStringId.'';
}
return $cellXMLFragment;
}
-
- /**
- * {@inheritdoc}
- */
- public function close(Worksheet $worksheet)
- {
- $worksheetFilePointer = $worksheet->getFilePointer();
-
- if (!\is_resource($worksheetFilePointer)) {
- return;
- }
-
- \fwrite($worksheetFilePointer, ' ');
- \fwrite($worksheetFilePointer, '');
- \fclose($worksheetFilePointer);
- }
}
diff --git a/lib/spout/src/Spout/Writer/XLSX/Writer.php b/lib/openspout/src/Writer/XLSX/Writer.php
similarity index 59%
rename from lib/spout/src/Spout/Writer/XLSX/Writer.php
rename to lib/openspout/src/Writer/XLSX/Writer.php
index 9d928fc944764..fa3a0548ba618 100644
--- a/lib/spout/src/Spout/Writer/XLSX/Writer.php
+++ b/lib/openspout/src/Writer/XLSX/Writer.php
@@ -1,13 +1,12 @@
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;
+ }
}
diff --git a/lib/spout/LICENSE b/lib/spout/LICENSE
deleted file mode 100644
index 167ec4d66df8f..0000000000000
--- a/lib/spout/LICENSE
+++ /dev/null
@@ -1,166 +0,0 @@
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
-TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-1. Definitions.
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
-2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
-3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
-4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
-5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
-6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
-7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
-8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
-9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
-END OF TERMS AND CONDITIONS
diff --git a/lib/spout/README.md b/lib/spout/README.md
deleted file mode 100644
index 7c772427cdc88..0000000000000
--- a/lib/spout/README.md
+++ /dev/null
@@ -1,63 +0,0 @@
-# Spout
-
-[![Latest Stable Version](https://poser.pugx.org/box/spout/v/stable)](https://packagist.org/packages/box/spout)
-[![Project Status](https://opensource.box.com/badges/active.svg)](https://opensource.box.com/badges)
-[![Build Status](https://travis-ci.org/box/spout.svg?branch=master)](https://travis-ci.org/box/spout)
-[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/box/spout/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/box/spout/?branch=master)
-[![Code Coverage](https://scrutinizer-ci.com/g/box/spout/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/box/spout/?branch=master)
-[![Total Downloads](https://poser.pugx.org/box/spout/downloads)](https://packagist.org/packages/box/spout)
-
-Spout is 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).
-
-Join the community and come discuss Spout: [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/box/spout?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
-
-
-## Documentation
-
-Full documentation can be found at [https://opensource.box.com/spout/](https://opensource.box.com/spout/).
-
-
-## Requirements
-
-* PHP version 7.2 or higher
-* PHP extension `php_zip` enabled
-* PHP extension `php_xmlreader` enabled
-
-## 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 `master` 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: [![Build Status](https://travis-ci.org/box/spout.svg?branch=perf-tests)](https://travis-ci.org/box/spout)
-
-
-## Support
-
-You can ask questions, submit new features ideas or discuss Spout in the chat room:
-[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/box/spout?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
-
-## Copyright and License
-
-Copyright 2017 Box, Inc. All rights reserved.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
diff --git a/lib/spout/readme_moodle.txt b/lib/spout/readme_moodle.txt
deleted file mode 100644
index 8b02de1fcdf24..0000000000000
--- a/lib/spout/readme_moodle.txt
+++ /dev/null
@@ -1,45 +0,0 @@
-Description of Spout library import
-=========================================
-* Download / Clone from https://github.com/box/spout/
-* Only include the src/Spout directory.
-* Update lib/thirdpartylibs.xml with the latest version.
-
-2022/11/25
-----------
-Imported PHP 8.1 patch from OpenSpout/OpenSpout 4.8.1
-https://github.com/openspout/openspout/commit/64a09a748d04992d63b38712599a9d8742bd77f7
-
-2022/10/27
-----------
-Changes:
-Box/Spout has been archived and is no longer maintained,
-MDL-73624 needs to fix with a couple of minor changes to
-Writer/WriterAbstract.php. The changes replace rawurldecode() with
-rawurlencode() in lines 143 and 144.
-by Meirza
-MDL-76494 compatibility for PHP 8.1
-
-2021/09/01
-----------
-Update to v3.3.0 (MDL-71707)
-by Paul Holden
-
-2020/12/07
-----------
-Update to v3.1.0 (MDL-70302)
-by Peter Dias
-
-2019/06/17
-----------
-Update to v3.0.1 (MDL-65762)
-by Adrian Greeve
-
-2017/10/10
-----------
-Updated to v2.7.3 (MDL-60288)
-by Ankit Agarwal
-
-2016/09/20
-----------
-Updated to v2.6.0 (MDL-56012)
-by Adrian Greeve
diff --git a/lib/spout/src/Spout/Autoloader/autoload.php b/lib/spout/src/Spout/Autoloader/autoload.php
deleted file mode 100644
index 44468a72815c4..0000000000000
--- a/lib/spout/src/Spout/Autoloader/autoload.php
+++ /dev/null
@@ -1,15 +0,0 @@
-register();
-$loader->addNamespace('Box\Spout', $srcBaseDirectory);
diff --git a/lib/spout/src/Spout/Common/Exception/EncodingConversionException.php b/lib/spout/src/Spout/Common/Exception/EncodingConversionException.php
deleted file mode 100644
index 098d0640491b7..0000000000000
--- a/lib/spout/src/Spout/Common/Exception/EncodingConversionException.php
+++ /dev/null
@@ -1,10 +0,0 @@
-createStringsEscaper();
-
- return new CellValueFormatter($sharedStringsManager, $styleManager, $shouldFormatDates, $shouldUse1904Dates, $escaper);
- }
-
- /**
- * @return Escaper\XLSX
- */
- public function createStringsEscaper()
- {
- /* @noinspection PhpUnnecessaryFullyQualifiedNameInspection */
- return new Escaper\XLSX();
- }
-}
diff --git a/lib/spout/src/Spout/Writer/Common/Creator/ManagerFactoryInterface.php b/lib/spout/src/Spout/Writer/Common/Creator/ManagerFactoryInterface.php
deleted file mode 100644
index 0eab2587969cb..0000000000000
--- a/lib/spout/src/Spout/Writer/Common/Creator/ManagerFactoryInterface.php
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-EOD;
-
- $content .= $this->getFontFaceSectionContent();
- $content .= $this->getStylesSectionContent();
- $content .= $this->getAutomaticStylesSectionContent($numWorksheets);
- $content .= $this->getMasterStylesSectionContent($numWorksheets);
-
- $content .= <<<'EOD'
-
-EOD;
-
- return $content;
- }
-
- /**
- * Returns the content of the "" section, inside "styles.xml" file.
- *
- * @return string
- */
- protected function getFontFaceSectionContent()
- {
- $content = '';
- foreach ($this->styleRegistry->getUsedFonts() as $fontName) {
- $content .= '';
- }
- $content .= '';
-
- return $content;
- }
-
- /**
- * Returns the content of the "" section, inside "styles.xml" file.
- *
- * @return string
- */
- protected function getStylesSectionContent()
- {
- $defaultStyle = $this->getDefaultStyle();
-
- return <<
-
-
-
-
-
-
-
-
-EOD;
- }
-
- /**
- * Returns the content of the "" section, inside "styles.xml" file.
- *
- * @param int $numWorksheets Number of worksheets created
- * @return string
- */
- protected function getAutomaticStylesSectionContent($numWorksheets)
- {
- $content = '';
-
- for ($i = 1; $i <= $numWorksheets; $i++) {
- $content .= <<
-
-
-
-
-EOD;
- }
-
- $content .= '';
-
- return $content;
- }
-
- /**
- * Returns the content of the "" section, inside "styles.xml" file.
- *
- * @param int $numWorksheets Number of worksheets created
- * @return string
- */
- protected function getMasterStylesSectionContent($numWorksheets)
- {
- $content = '';
-
- for ($i = 1; $i <= $numWorksheets; $i++) {
- $content .= <<
-
-
-
-
-
-EOD;
- }
-
- $content .= '';
-
- return $content;
- }
-
- /**
- * Returns the contents of the "" section, inside "content.xml" file.
- *
- * @return string
- */
- public function getContentXmlFontFaceSectionContent()
- {
- $content = '';
- foreach ($this->styleRegistry->getUsedFonts() as $fontName) {
- $content .= '';
- }
- $content .= '';
-
- return $content;
- }
-
- /**
- * Returns the contents of the "" section, inside "content.xml" file.
- *
- * @param Worksheet[] $worksheets
- * @return string
- */
- public function getContentXmlAutomaticStylesSectionContent($worksheets)
- {
- $content = '';
-
- foreach ($this->styleRegistry->getRegisteredStyles() as $style) {
- $content .= $this->getStyleSectionContent($style);
- }
-
- $content .= <<<'EOD'
-
-
-
-
-
-
-EOD;
-
- foreach ($worksheets as $worksheet) {
- $worksheetId = $worksheet->getId();
- $isSheetVisible = $worksheet->getExternalSheet()->isVisible() ? 'true' : 'false';
-
- $content .= <<
-
-
-EOD;
- }
-
- $content .= '';
-
- return $content;
- }
-
- /**
- * Returns the contents of the "" section, inside "" section
- *
- * @param \Box\Spout\Common\Entity\Style\Style $style
- * @return string
- */
- protected function getStyleSectionContent($style)
- {
- $styleIndex = $style->getId() + 1; // 1-based
-
- $content = '';
-
- $content .= $this->getTextPropertiesSectionContent($style);
- $content .= $this->getParagraphPropertiesSectionContent($style);
- $content .= $this->getTableCellPropertiesSectionContent($style);
-
- $content .= '';
-
- return $content;
- }
-
- /**
- * Returns the contents of the "" section, inside "" section
- *
- * @param \Box\Spout\Common\Entity\Style\Style $style
- * @return string
- */
- private function getTextPropertiesSectionContent($style)
- {
- if (!$style->shouldApplyFont()) {
- return '';
- }
-
- return 'getFontSectionContent($style)
- . '/>';
- }
-
- /**
- * Returns the contents of the fonts definition section, inside "" section
- *
- * @param \Box\Spout\Common\Entity\Style\Style $style
- *
- * @return string
- */
- private function getFontSectionContent($style)
- {
- $defaultStyle = $this->getDefaultStyle();
- $content = '';
-
- $fontColor = $style->getFontColor();
- if ($fontColor !== $defaultStyle->getFontColor()) {
- $content .= ' fo:color="#' . $fontColor . '"';
- }
-
- $fontName = $style->getFontName();
- if ($fontName !== $defaultStyle->getFontName()) {
- $content .= ' style:font-name="' . $fontName . '" style:font-name-asian="' . $fontName . '" style:font-name-complex="' . $fontName . '"';
- }
-
- $fontSize = $style->getFontSize();
- if ($fontSize !== $defaultStyle->getFontSize()) {
- $content .= ' fo:font-size="' . $fontSize . 'pt" style:font-size-asian="' . $fontSize . 'pt" style:font-size-complex="' . $fontSize . 'pt"';
- }
-
- if ($style->isFontBold()) {
- $content .= ' fo:font-weight="bold" style:font-weight-asian="bold" style:font-weight-complex="bold"';
- }
- if ($style->isFontItalic()) {
- $content .= ' fo:font-style="italic" style:font-style-asian="italic" style:font-style-complex="italic"';
- }
- if ($style->isFontUnderline()) {
- $content .= ' style:text-underline-style="solid" style:text-underline-type="single"';
- }
- if ($style->isFontStrikethrough()) {
- $content .= ' style:text-line-through-style="solid"';
- }
-
- return $content;
- }
-
- /**
- * Returns the contents of the "" section, inside "" section
- *
- * @param \Box\Spout\Common\Entity\Style\Style $style
- *
- * @return string
- */
- private function getParagraphPropertiesSectionContent($style)
- {
- if (!$style->shouldApplyCellAlignment()) {
- return '';
- }
-
- return 'getCellAlignmentSectionContent($style)
- . '/>';
- }
-
- /**
- * Returns the contents of the cell alignment definition for the "" section
- *
- * @param \Box\Spout\Common\Entity\Style\Style $style
- *
- * @return string
- */
- private function getCellAlignmentSectionContent($style)
- {
- return \sprintf(
- ' fo:text-align="%s" ',
- $this->transformCellAlignment($style->getCellAlignment())
- );
- }
-
- /**
- * Even though "left" and "right" alignments are part of the spec, and interpreted
- * respectively as "start" and "end", using the recommended values increase compatibility
- * with software that will read the created ODS file.
- *
- * @param string $cellAlignment
- *
- * @return string
- */
- private function transformCellAlignment($cellAlignment)
- {
- switch ($cellAlignment) {
- case CellAlignment::LEFT: return 'start';
- case CellAlignment::RIGHT: return 'end';
- default: return $cellAlignment;
- }
- }
-
- /**
- * Returns the contents of the "" section, inside "" section
- *
- * @param \Box\Spout\Common\Entity\Style\Style $style
- * @return string
- */
- private function getTableCellPropertiesSectionContent($style)
- {
- $content = 'shouldWrapText()) {
- $content .= $this->getWrapTextXMLContent();
- }
-
- if ($style->shouldApplyBorder()) {
- $content .= $this->getBorderXMLContent($style);
- }
-
- if ($style->shouldApplyBackgroundColor()) {
- $content .= $this->getBackgroundColorXMLContent($style);
- }
-
- $content .= '/>';
-
- return $content;
- }
-
- /**
- * Returns the contents of the wrap text definition for the "" section
- *
- * @return string
- */
- private function getWrapTextXMLContent()
- {
- return ' fo:wrap-option="wrap" style:vertical-align="automatic" ';
- }
-
- /**
- * Returns the contents of the borders definition for the "" section
- *
- * @param \Box\Spout\Common\Entity\Style\Style $style
- * @return string
- */
- private function getBorderXMLContent($style)
- {
- $borders = \array_map(function (BorderPart $borderPart) {
- return BorderHelper::serializeBorderPart($borderPart);
- }, $style->getBorder()->getParts());
-
- return \sprintf(' %s ', \implode(' ', $borders));
- }
-
- /**
- * Returns the contents of the background color definition for the "" section
- *
- * @param \Box\Spout\Common\Entity\Style\Style $style
- * @return string
- */
- private function getBackgroundColorXMLContent($style)
- {
- return \sprintf(' fo:background-color="#%s" ', $style->getBackgroundColor());
- }
-}
diff --git a/lib/thirdpartylibs.xml b/lib/thirdpartylibs.xml
index da2006dd353a9..a38fd0c8ce7dd 100644
--- a/lib/thirdpartylibs.xml
+++ b/lib/thirdpartylibs.xml
@@ -374,15 +374,14 @@ All rights reserved.
- spout
- Spout
- Library for importing and exporting csv / excel / ODS files.
- 3.3.0
- Apache
- 2.0
- https://github.com/box/spout/
+ openspout
+ OpenSpout
+ Library to read and write spreadsheet files (CSV, XLSX and ODS).
+ 3.7.3
+ MIT
+ https://github.com/openspout/openspout
- 2022 Box, Inc. All rights reserved
+ OpenSpout
diff --git a/lib/upgrade.txt b/lib/upgrade.txt
index 99374f2552e99..0875b604323b3 100644
--- a/lib/upgrade.txt
+++ b/lib/upgrade.txt
@@ -26,6 +26,7 @@ information provided here is intended especially for developers.
initials bar without the bootstrapping and form handling on each initials bar. If you use this mini render,
you'll need to implement your own form handling. Example usage can be found within the grader report.
* There is a new helper function mtrace_exception to help with reporting exceptions you have caught in scheduled tasks.
+* Box/Spout has been archived and is no longer maintained, so it has now been removed and replaced by OpenSpout.
=== 4.1 ===
| |