Compare commits

..

8 Commits
php74 ... php82

Author SHA1 Message Date
008fad3b69 maj mergeCells 2025-02-01 11:02:40 +04:00
909cd849cc ajout rotation et autofit 2025-02-01 11:02:40 +04:00
b721c16335 support pageOrder 2025-02-01 11:02:40 +04:00
2f3865addc maj options 4.x 2025-02-01 11:02:40 +04:00
37ce992b3e application des modifs nulib/spout pour v4.27.0 2025-02-01 11:02:40 +04:00
09540b1767 importation upstream 4.27.0 2025-02-01 11:02:40 +04:00
86f34af6f6 supprimer upstream-3.x 2025-02-01 11:02:40 +04:00
53188fd2d8 maj deps php8.2 2025-02-01 11:02:40 +04:00
250 changed files with 7743 additions and 9565 deletions

View File

@ -4,5 +4,5 @@
RUNPHP= RUNPHP=
# Si RUNPHP n'est pas défini, les variables suivantes peuvent être définies # Si RUNPHP n'est pas défini, les variables suivantes peuvent être définies
DIST=d11 DIST=d12
#REGISTRY=pubdocker.univ-reunion.fr/dist #REGISTRY=pubdocker.univ-reunion.fr

View File

@ -19,28 +19,32 @@
} }
}, },
"replace": { "replace": {
"openspout/openspout": "v3.7.4" "openspout/openspout": "v4.27.0"
}, },
"require": { "require": {
"nulib/php": "^7.4-dev", "nulib/php": "^8.2-dev",
"ext-dom": "*", "ext-dom": "*",
"ext-fileinfo": "*",
"ext-filter": "*", "ext-filter": "*",
"ext-libxml": "*", "ext-libxml": "*",
"ext-xmlreader": "*", "ext-xmlreader": "*",
"ext-zip": "*", "ext-zip": "*",
"php": "^7.4" "php": "^8.2"
}, },
"require-dev": { "require-dev": {
"nulib/tests": "^7.4", "nulib/tests": "^8.2",
"friendsofphp/php-cs-fixer": "^3.4", "friendsofphp/php-cs-fixer": "^3.64.0",
"phpstan/phpstan": "^1.4", "infection/infection": "^0.29.6",
"phpstan/phpstan-phpunit": "^1.0", "phpbench/phpbench": "^1.3.1",
"phpstan/phpstan": "^1.12.4",
"phpstan/phpstan-phpunit": "^1.4.0",
"phpstan/phpstan-strict-rules": "^1.6.1",
"ext-zlib": "*" "ext-zlib": "*"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"nulib\\": "src", "nulib\\": "src",
"OpenSpout\\": "upstream-3.x/src" "OpenSpout\\": "upstream-4.x/src"
} }
}, },
"autoload-dev": { "autoload-dev": {

91
patches/v4.27.0.patch Normal file
View File

@ -0,0 +1,91 @@
diff --git a/src/Reader/XLSX/Helper/CellValueFormatter.php b/src/Reader/XLSX/Helper/CellValueFormatter.php
index 776de0a..bc7a5c4 100644
--- a/src/Reader/XLSX/Helper/CellValueFormatter.php
+++ b/src/Reader/XLSX/Helper/CellValueFormatter.php
@@ -284,9 +284,13 @@ final class CellValueFormatter
\assert(false !== $dateObj);
if ($this->shouldFormatDates) {
- $styleNumberFormatCode = $this->styleManager->getNumberFormatCode($cellStyleId);
- $phpDateFormat = DateFormatHelper::toPHPDateFormat($styleNumberFormatCode);
+ //$styleNumberFormatCode = $this->styleManager->getNumberFormatCode($cellStyleId);
+ //$phpDateFormat = DateFormatHelper::toPHPDateFormat($styleNumberFormatCode);
+ // Toujours utiliser le format français complet
+ $phpDateFormat = "d/m/Y H:i:s";
$cellValue = $dateObj->format($phpDateFormat);
+ // Enlever la composante heure si elle n'existe pas
+ $cellValue = preg_replace('/ 00:00:00$/', "", $cellValue);
} else {
$cellValue = $dateObj;
}
diff --git a/src/Reader/XLSX/Options.php b/src/Reader/XLSX/Options.php
index 636d2a4..e8609b5 100644
--- a/src/Reader/XLSX/Options.php
+++ b/src/Reader/XLSX/Options.php
@@ -10,7 +10,7 @@ final class Options
{
use TempFolderOptionTrait;
- public bool $SHOULD_FORMAT_DATES = false;
+ public bool $SHOULD_FORMAT_DATES = true;
public bool $SHOULD_PRESERVE_EMPTY_ROWS = false;
public bool $SHOULD_USE_1904_DATES = false;
public bool $SHOULD_LOAD_MERGE_CELLS = false;
diff --git a/src/Writer/AbstractWriter.php b/src/Writer/AbstractWriter.php
index eef34fb..a22e90f 100644
--- a/src/Writer/AbstractWriter.php
+++ b/src/Writer/AbstractWriter.php
@@ -13,6 +13,12 @@ abstract class AbstractWriter implements WriterInterface
/** @var resource Pointer to the file/stream we will write to */
protected $filePointer;
+ /**
+ * @var bool faut-il garder ouvert le flux quand {@link self::close()} est
+ * appelé?
+ */
+ protected bool $dontCloseFilePointer = false;
+
/** @var string document creator */
protected string $creator = 'OpenSpout';
@@ -20,7 +26,7 @@ abstract class AbstractWriter implements WriterInterface
protected static string $headerContentType;
/** @var string Path to the output file */
- private string $outputFilePath;
+ private ?string $outputFilePath;
/** @var bool Indicates whether the writer has been opened or not */
private bool $isWriterOpened = false;
@@ -51,6 +57,20 @@ abstract class AbstractWriter implements WriterInterface
$this->isWriterOpened = true;
}
+ final public function writeToStream($filePointer): void
+ {
+ if (!is_resource($filePointer)) {
+ throw new IOException("filePointer is not a resource");
+ }
+ $this->outputFilePath = null;
+
+ $this->filePointer = $filePointer;
+ $this->dontCloseFilePointer = true;
+
+ $this->openWriter();
+ $this->isWriterOpened = true;
+ }
+
/**
* @codeCoverageIgnore
*
@@ -140,7 +160,9 @@ abstract class AbstractWriter implements WriterInterface
$this->closeWriter();
- fclose($this->filePointer);
+ if (!$this->dontCloseFilePointer) {
+ fclose($this->filePointer);
+ }
$this->isWriterOpened = false;
}

View File

@ -14,14 +14,22 @@ use nulib\ref\ext\spout\ref_builder_ods;
use nulib\ref\ext\spout\ref_builder_xlsx; use nulib\ref\ext\spout\ref_builder_xlsx;
use nulib\str; use nulib\str;
use nulib\web\http; use nulib\web\http;
use OpenSpout\Common\Entity\Cell;
use OpenSpout\Common\Entity\Style\Border; use OpenSpout\Common\Entity\Style\Border;
use OpenSpout\Common\Entity\Style\BorderPart; use OpenSpout\Common\Entity\Style\BorderPart;
use OpenSpout\Common\Entity\Cell\DateTimeCell;
use OpenSpout\Common\Entity\Cell\EmptyCell;
use OpenSpout\Common\Entity\Cell\NumericCell;
use OpenSpout\Common\Entity\Cell\StringCell;
use OpenSpout\Common\Entity\Row;
use OpenSpout\Common\Entity\Style\Color;
use OpenSpout\Common\Entity\Style\Style; use OpenSpout\Common\Entity\Style\Style;
use OpenSpout\Common\Helper\CellTypeHelper; use OpenSpout\Writer\AbstractWriterMultiSheets;
use OpenSpout\Writer\Common\Creator\WriterEntityFactory; use OpenSpout\Writer\Common\AbstractOptions;
use OpenSpout\Writer\WriterMultiSheetsAbstract; use OpenSpout\Writer\ODS\Options as ODSOptions;
use OpenSpout\Writer\ODS\Writer as ODSWriter;
use OpenSpout\Writer\XLSX\Entity\SheetView; use OpenSpout\Writer\XLSX\Entity\SheetView;
use OpenSpout\Writer\XLSX\Options as XLSXOptions;
use OpenSpout\Writer\XLSX\Writer as XLSXWriter;
class SpoutBuilder extends AbstractBuilder { class SpoutBuilder extends AbstractBuilder {
use TAbstractBuilder; use TAbstractBuilder;
@ -42,16 +50,16 @@ class SpoutBuilder extends AbstractBuilder {
return $object; return $object;
} }
protected static function add_border_part(?Border &$border, string $name, ?array $params): void { protected static function add_border_part(?array &$parts, string $name, ?array $params): void {
if ($params === null) return; if ($params === null) return;
if ($border === null) $border = new Border();
$part = new BorderPart($name);
if (($color = $params["color"] ?? null) !== null) { if (($color = $params["color"] ?? null) !== null) {
$part->setColor(cl::get(ref_builder::COLORS, $color, $color)); $color = cl::get(ref_builder::COLORS, $color, $color);
} else {
$color = Color::BLACK;
} }
if (($width = $params["width"] ?? null) !== null) $part->setWidth($width); if (($width = $params["width"] ?? null) === null) $width = Border::WIDTH_MEDIUM;
if (($style = $params["style"] ?? null) !== null) $part->setStyle($style); if (($style = $params["style"] ?? null) === null) $style = Border::STYLE_SOLID;
$border->addPart($part); $parts[] = new BorderPart($name, $color, $width, $style);
} }
protected static function set_defaults(?array &$params, string $key, array $defaults): void { protected static function set_defaults(?array &$params, string $key, array $defaults): void {
@ -82,8 +90,9 @@ class SpoutBuilder extends AbstractBuilder {
$style->setBackgroundColor(cl::get(ref_builder::COLORS, $color, $color)); $style->setBackgroundColor(cl::get(ref_builder::COLORS, $color, $color));
} }
if (($align = $cell["align"] ?? null) !== null) $style->setCellAlignment($align); if (($align = $cell["align"] ?? null) !== null) $style->setCellAlignment($align);
//if (($align = $cell["valign"] ?? null) !== null) $style->setCellVerticalAlignment($align); if (($align = $cell["valign"] ?? null) !== null) $style->setCellVerticalAlignment($align);
if (($wrap = $cell["wrap"] ?? null) !== null) $style->setShouldWrapText($wrap); if (($wrap = $cell["wrap"] ?? null) !== null) $style->setShouldWrapText($wrap);
if (($rotation = $cell["rotation"] ?? null) !== null) $style->setTextRotation($rotation);
if (($format = $cell["format"] ?? null) !== null) $style->setFormat($format); if (($format = $cell["format"] ?? null) !== null) $style->setFormat($format);
if (($border = $cell["border"] ?? null) !== null) { if (($border = $cell["border"] ?? null) !== null) {
if (is_string($border)) { if (is_string($border)) {
@ -114,18 +123,14 @@ class SpoutBuilder extends AbstractBuilder {
if ($colorAll !== null) $part["color"] = $colorAll; if ($colorAll !== null) $part["color"] = $colorAll;
}; unset($part); }; unset($part);
} }
$parts = null;
$top = $border["top"] ?? null; self::add_border_part($parts, "top", $border["top"] ?? null);
$right = $border["right"] ?? null; self::add_border_part($parts, "right", $border["right"] ?? null);
$bottom = $border["bottom"] ?? null; self::add_border_part($parts, "bottom", $border["bottom"] ?? null);
$left = $border["left"] ?? null; self::add_border_part($parts, "left", $border["left"] ?? null);
$border = null; if ($parts !== null) $style->setBorder(new Border(...$parts));
self::add_border_part($border, "top", $top);
self::add_border_part($border, "right", $right);
self::add_border_part($border, "bottom", $bottom);
self::add_border_part($border, "left", $left);
if ($border !== null) $style->setBorder($border);
} }
if (($autofit = $cell["autofit"] ?? null) !== null) $style->setShouldShrinkToFit($autofit);
return $style; return $style;
} }
@ -173,26 +178,31 @@ class SpoutBuilder extends AbstractBuilder {
case "ods": case "ods":
case self::SS_TYPE_ODS: case self::SS_TYPE_ODS:
$ssType = self::SS_TYPE_ODS; $ssType = self::SS_TYPE_ODS;
$ssWriter = WriterEntityFactory::createODSWriter(); $ssOptions = new ODSOptions();
self::apply_params($ssWriter, $spoutParams, ref_builder_ods::PARAMS_SPOUT); $ssWriterClass = ODSWriter::class;
$refParams = ref_builder_ods::PARAMS_SPOUT;
break; break;
case "xlsx": case "xlsx":
case self::SS_TYPE_XLSX: case self::SS_TYPE_XLSX:
default: default:
$ssType = self::SS_TYPE_XLSX; $ssType = self::SS_TYPE_XLSX;
$ssWriter = WriterEntityFactory::createXLSXWriter(); $ssOptions = new XLSXOptions();
self::apply_params($ssWriter, $spoutParams, ref_builder_xlsx::PARAMS_SPOUT); $ssWriterClass = XLSXWriter::class;
break; $refParams = ref_builder_xlsx::PARAMS_SPOUT;
break;
} }
$defaultColumnWidth = $spoutParams["default_column_width"] ?? null; $defaultColumnWidth = $spoutParams["default_column_width"] ?? null;
if ($defaultColumnWidth !== null) $ssWriter->setDefaultColumnWidth($defaultColumnWidth); if ($defaultColumnWidth !== null) $ssOptions->DEFAULT_COLUMN_WIDTH = $defaultColumnWidth;
$defaultRowHeight = $spoutParams["default_row_height"] ?? null; $defaultRowHeight = $spoutParams["default_row_height"] ?? null;
if ($defaultRowHeight !== null) $ssWriter->setDefaultRowHeight($defaultRowHeight); if ($defaultRowHeight !== null) $ssOptions->DEFAULT_ROW_HEIGHT = $defaultRowHeight;
$defaultRowStyle = $spoutParams["default_row_style"] ?? null; $defaultRowStyle = $spoutParams["default_row_style"] ?? null;
if ($defaultRowStyle !== null) $ssWriter->setDefaultRowStyle($defaultRowStyle); if ($defaultRowStyle !== null) $ssOptions->DEFAULT_ROW_STYLE = $defaultRowStyle;
self::apply_params($ssOptions, $spoutParams, $refParams);
$ssWriter = new $ssWriterClass($ssOptions);
$ssWriter->writeToStream($this->getResource()); $ssWriter->writeToStream($this->getResource());
$this->ssType = $ssType; $this->ssType = $ssType;
$this->ssOptions = $ssOptions;
$this->ssWriter = $ssWriter; $this->ssWriter = $ssWriter;
$this->spoutParams = $spoutParams; $this->spoutParams = $spoutParams;
$this->typeNumeric = boolval($params["type_numeric"] ?? static::TYPE_NUMERIC); $this->typeNumeric = boolval($params["type_numeric"] ?? static::TYPE_NUMERIC);
@ -213,7 +223,9 @@ class SpoutBuilder extends AbstractBuilder {
/** @var int type de fichier généré */ /** @var int type de fichier généré */
protected int $ssType; protected int $ssType;
protected WriterMultiSheetsAbstract $ssWriter; protected AbstractOptions $ssOptions;
protected AbstractWriterMultiSheets $ssWriter;
protected ?array $spoutParams; protected ?array $spoutParams;
@ -338,7 +350,8 @@ class SpoutBuilder extends AbstractBuilder {
* Les lignes sont indexées sur 1 * Les lignes sont indexées sur 1
*/ */
function mergeCells(int $topLeftCol, int $topLeftRow, int $bottomRightCol, int $bottomRightRow): void { function mergeCells(int $topLeftCol, int $topLeftRow, int $bottomRightCol, int $bottomRightRow): void {
$this->ssWriter->mergeCells([$topLeftCol, $topLeftRow], [$bottomRightCol, $bottomRightRow]); $sheet = $this->ssWriter->getCurrentSheet();
$this->ssOptions->mergeCells($topLeftCol, $topLeftRow, $bottomRightCol, $bottomRightRow, $sheet->getIndex());
} }
protected function isNumeric($value): bool { protected function isNumeric($value): bool {
@ -356,10 +369,6 @@ class SpoutBuilder extends AbstractBuilder {
$style ??= new Style(); $style ??= new Style();
$style->setFormat(self::DATETIME_FORMAT); $style->setFormat(self::DATETIME_FORMAT);
return true; return true;
} elseif (CellTypeHelper::isDateTimeOrDateInterval($value)) {
$style ??= new Style();
$style->setFormat(self::DATE_FORMAT);
return true;
} }
if (!is_string($value) || !$this->typeDate) return false; if (!is_string($value) || !$this->typeDate) return false;
if (DateTime::isa_datetime($value, true)) { if (DateTime::isa_datetime($value, true)) {
@ -401,16 +410,14 @@ class SpoutBuilder extends AbstractBuilder {
$style = $colStyles[$key] ?? null; $style = $colStyles[$key] ?? null;
self::ensure_style($style); self::ensure_style($style);
if ($col === null || $col === "") { if ($col === null || $col === "") {
$type = Cell::TYPE_EMPTY; $cell = new EmptyCell(null, $style);
} elseif ($this->isNumeric($col)) { } elseif ($this->isNumeric($col)) {
$type = Cell::TYPE_NUMERIC; $cell = new NumericCell($col, $style);
} elseif ($this->isDate($col, $style)) { } elseif ($this->isDate($col, $style)) {
$type = Cell::TYPE_DATE; $cell = new DateTimeCell($col, $style);
} else { } else {
$type = Cell::TYPE_STRING; $cell = new StringCell($col, $style);
} }
$cell = WriterEntityFactory::createCell($col, $style);
$cell->setType($type);
$cells[] = $cell; $cells[] = $cell;
} }
@ -421,7 +428,7 @@ class SpoutBuilder extends AbstractBuilder {
} }
$rowStyle ??= $oddStyle; $rowStyle ??= $oddStyle;
self::ensure_style($rowStyle); self::ensure_style($rowStyle);
$row = WriterEntityFactory::createRow($cells, $rowStyle); $row = new Row($cells, $rowStyle);
self::apply_params($row, $rowParams, ref_builder::ROW_PARAMS); self::apply_params($row, $rowParams, ref_builder::ROW_PARAMS);
$mergeCells = $rowParams["merge_cells"] ?? null; $mergeCells = $rowParams["merge_cells"] ?? null;

View File

@ -82,6 +82,7 @@ class ref_builder {
"align" => "string", "align" => "string",
"valign" => "string", "valign" => "string",
"wrap" => "bool", "wrap" => "bool",
"rotation" => "int",
"format" => "string", "format" => "string",
"border" => [ "border" => [
"top" => ["color" => "string", "width" => "string", "style" => "string"], "top" => ["color" => "string", "width" => "string", "style" => "string"],
@ -89,5 +90,6 @@ class ref_builder {
"bottom" => ["color" => "string", "width" => "string", "style" => "string"], "bottom" => ["color" => "string", "width" => "string", "style" => "string"],
"left" => ["color" => "string", "width" => "string", "style" => "string"], "left" => ["color" => "string", "width" => "string", "style" => "string"],
], ],
"autofit" => "bool",
]; ];
} }

View File

@ -1,16 +1,43 @@
<?php <?php
namespace nulib\ref\ext\spout; namespace nulib\ref\ext\spout;
use OpenSpout\Writer\XLSX\Options\HeaderFooter;
use OpenSpout\Writer\XLSX\Options\PageMargin;
use OpenSpout\Writer\XLSX\Options\PageSetup;
class ref_builder_xlsx extends ref_builder { class ref_builder_xlsx extends ref_builder {
const PARAMS_SPOUT = [
...parent::PARAMS_SPOUT,
"->setPageSetup" => [
PageSetup::class,
"page_orientation" => "string",
"page_size" => "string",
"fit_to_height" => "bool",
"fit_to_width" => "bool",
"page_order" => "string",
],
"->setPageMargin" => [
PageMargin::class,
"top" => "float",
"right" => "float",
"bottom" => "float",
"left" => "float",
"header" => "float",
"footer" => "float",
],
"->setHeaderFooter" => [
HeaderFooter::class,
"odd_header" => "string",
"odd_footer" => "string",
"even_header" => "string",
"even_footer" => "string",
"different_odd_even" => "bool",
],
];
const PARAMS_SHEET = [ const PARAMS_SHEET = [
...parent::PARAMS_SHEET,
"view" => self::PARAMS_SHEET_VIEW, "view" => self::PARAMS_SHEET_VIEW,
# copie de parent::SHEET
"->setName" => ["string"],
"->setIsVisible" => ["bool"],
"header_style" => self::STYLE,
"odd_style" => self::STYLE,
"even_style" => self::STYLE,
"different_odd_even" => "bool",
]; ];
const PARAMS_SHEET_VIEW = [ const PARAMS_SHEET_VIEW = [

View File

@ -1,54 +0,0 @@
# 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

View File

@ -1,147 +0,0 @@
<?php
namespace OpenSpout\Autoloader;
/**
* @see https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader-examples.md#class-example
*/
class Psr4Autoloader
{
/**
* An associative array where the key is a namespace prefix and the value
* is an array of base directories for classes in that namespace.
*
* @var array
*/
protected $prefixes = [];
/**
* Register loader with SPL autoloader stack.
*/
public function register()
{
spl_autoload_register([$this, 'loadClass']);
}
/**
* Adds a base directory for a namespace prefix.
*
* @param string $prefix the namespace prefix
* @param string $baseDir a base directory for class files in the
* namespace
* @param bool $prepend if true, prepend the base directory to the stack
* instead of appending it; this causes it to be searched first rather
* than last
*/
public function addNamespace($prefix, $baseDir, $prepend = false)
{
// normalize namespace prefix
$prefix = trim($prefix, '\\').'\\';
// normalize the base directory with a trailing separator
$baseDir = rtrim($baseDir, \DIRECTORY_SEPARATOR).'/';
// initialize the namespace prefix array
if (false === isset($this->prefixes[$prefix])) {
$this->prefixes[$prefix] = [];
}
// retain the base directory for the namespace prefix
if ($prepend) {
array_unshift($this->prefixes[$prefix], $baseDir);
} else {
$this->prefixes[$prefix][] = $baseDir;
}
}
/**
* Loads the class file for a given class name.
*
* @param string $class the fully-qualified class name
*
* @return mixed the mapped file name on success, or boolean false on
* failure
*/
public function loadClass($class)
{
// the current namespace prefix
$prefix = $class;
// work backwards through the namespace names of the fully-qualified
// class name to find a mapped file name
while (($pos = strrpos($prefix, '\\')) !== false) {
// retain the trailing namespace separator in the prefix
$prefix = substr($class, 0, $pos + 1);
// the rest is the relative class name
$relativeClass = substr($class, $pos + 1);
// try to load a mapped file for the prefix and relative class
$mappedFile = $this->loadMappedFile($prefix, $relativeClass);
if (false !== $mappedFile) {
return $mappedFile;
}
// remove the trailing namespace separator for the next iteration
// of strrpos()
$prefix = rtrim($prefix, '\\');
}
// never found a mapped file
return false;
}
/**
* Load the mapped file for a namespace prefix and relative class.
*
* @param string $prefix the namespace prefix
* @param string $relativeClass the relative class name
*
* @return mixed boolean false if no mapped file can be loaded, or the
* name of the mapped file that was loaded
*/
protected function loadMappedFile($prefix, $relativeClass)
{
// are there any base directories for this namespace prefix?
if (false === isset($this->prefixes[$prefix])) {
return false;
}
// look through base directories for this namespace prefix
foreach ($this->prefixes[$prefix] as $baseDir) {
// replace the namespace prefix with the base directory,
// replace namespace separators with directory separators
// in the relative class name, append with .php
$file = $baseDir
.str_replace('\\', '/', $relativeClass)
.'.php';
// if the mapped file exists, require it
if ($this->requireFile($file)) {
// yes, we're done
return $file;
}
}
// never found it
return false;
}
/**
* If a file exists, require it from the file system.
*
* @param string $file the file to require
*
* @return bool true if the file exists, false if not
*/
protected function requireFile($file)
{
if (file_exists($file)) {
require $file;
return true;
}
return false;
}
}

View File

@ -1,15 +0,0 @@
<?php
namespace OpenSpout\Autoloader;
require_once 'Psr4Autoloader.php';
/**
* @var string
* Full path to "src/Spout" which is what we want "OpenSpout" to map to
*/
$srcBaseDirectory = \dirname(__DIR__);
$loader = new Psr4Autoloader();
$loader->register();
$loader->addNamespace('OpenSpout', $srcBaseDirectory);

View File

@ -1,48 +0,0 @@
<?php
namespace OpenSpout\Common\Creator;
use OpenSpout\Common\Helper\EncodingHelper;
use OpenSpout\Common\Helper\FileSystemHelper;
use OpenSpout\Common\Helper\GlobalFunctionsHelper;
use OpenSpout\Common\Helper\StringHelper;
/**
* Factory to create helpers.
*/
class HelperFactory
{
/**
* @return GlobalFunctionsHelper
*/
public function createGlobalFunctionsHelper()
{
return new GlobalFunctionsHelper();
}
/**
* @param string $baseFolderPath The path of the base folder where all the I/O can occur
*
* @return FileSystemHelper
*/
public function createFileSystemHelper($baseFolderPath)
{
return new FileSystemHelper($baseFolderPath);
}
/**
* @return EncodingHelper
*/
public function createEncodingHelper(GlobalFunctionsHelper $globalFunctionsHelper)
{
return new EncodingHelper($globalFunctionsHelper);
}
/**
* @return StringHelper
*/
public function createStringHelper()
{
return new StringHelper();
}
}

View File

@ -1,227 +0,0 @@
<?php
namespace OpenSpout\Common\Entity;
use OpenSpout\Common\Entity\Style\Style;
use OpenSpout\Common\Helper\CellTypeHelper;
class Cell
{
/**
* Numeric cell type (whole numbers, fractional numbers, dates).
*/
public const TYPE_NUMERIC = 0;
/**
* String (text) cell type.
*/
public const TYPE_STRING = 1;
/**
* Formula cell type
* Not used at the moment.
*/
public const TYPE_FORMULA = 2;
/**
* Empty cell type.
*/
public const TYPE_EMPTY = 3;
/**
* Boolean cell type.
*/
public const TYPE_BOOLEAN = 4;
/**
* Date cell type.
*/
public const TYPE_DATE = 5;
/**
* Error cell type.
*/
public const TYPE_ERROR = 6;
/**
* The value of this cell.
*
* @var null|mixed
*/
protected $value;
/**
* The cell type.
*
* @var null|int
*/
protected $type;
/**
* The cell style.
*
* @var Style
*/
protected $style;
/**
* @param null|mixed $value
*/
public function __construct($value, Style $style = null)
{
$this->setValue($value);
$this->setStyle($style);
}
/**
* @return string
*/
public function __toString()
{
return (string) $this->getValue();
}
/**
* @param null|mixed $value
*/
public function setValue($value)
{
$this->value = $value;
$this->type = $this->detectType($value);
}
/**
* @return null|mixed
*/
public function getValue()
{
return !$this->isError() ? $this->value : null;
}
/**
* @return mixed
*/
public function getValueEvenIfError()
{
return $this->value;
}
/**
* @param null|Style $style
*/
public function setStyle($style)
{
$this->style = $style ?: new Style();
}
/**
* @return Style
*/
public function getStyle()
{
return $this->style;
}
/**
* @return null|int
*/
public function getType()
{
return $this->type;
}
/**
* @param int $type
*/
public function setType($type)
{
$this->type = $type;
}
/**
* @return bool
*/
public function isBoolean()
{
return self::TYPE_BOOLEAN === $this->type;
}
/**
* @return bool
*/
public function isEmpty()
{
return self::TYPE_EMPTY === $this->type;
}
/**
* @return bool
*/
public function isNumeric()
{
return self::TYPE_NUMERIC === $this->type;
}
/**
* @return bool
*/
public function isString()
{
return self::TYPE_STRING === $this->type;
}
/**
* @return bool
*/
public function isDate()
{
return self::TYPE_DATE === $this->type;
}
/**
* @return bool
*/
public function isFormula()
{
return self::TYPE_FORMULA === $this->type;
}
/**
* @return bool
*/
public function isError()
{
return self::TYPE_ERROR === $this->type;
}
/**
* Get the current value type.
*
* @param null|mixed $value
*
* @return int
*/
protected function detectType($value)
{
if (CellTypeHelper::isBoolean($value)) {
return self::TYPE_BOOLEAN;
}
if (CellTypeHelper::isEmpty($value)) {
return self::TYPE_EMPTY;
}
if (CellTypeHelper::isNumeric($value)) {
return self::TYPE_NUMERIC;
}
if (CellTypeHelper::isDateTimeOrDateInterval($value)) {
return self::TYPE_DATE;
}
if (CellTypeHelper::isFormula($value)) {
return self::TYPE_FORMULA;
}
if (CellTypeHelper::isNonEmptyString($value)) {
return self::TYPE_STRING;
}
return self::TYPE_ERROR;
}
}

View File

@ -1,166 +0,0 @@
<?php
namespace OpenSpout\Common\Entity;
use OpenSpout\Common\Entity\Style\Style;
class Row
{
/**
* The cells in this row.
*
* @var Cell[]
*/
protected $cells = [];
/**
* The row style.
*
* @var Style
*/
protected $style;
/**
* Row height (default is 15).
*
* @var string
*/
protected $height = '15';
/**
* Row constructor.
*
* @param Cell[] $cells
* @param null|Style $style
*/
public function __construct(array $cells, $style)
{
$this
->setCells($cells)
->setStyle($style)
;
}
/**
* @return Cell[] $cells
*/
public function getCells()
{
return $this->cells;
}
/**
* @param Cell[] $cells
*
* @return Row
*/
public function setCells(array $cells)
{
$this->cells = [];
foreach ($cells as $cell) {
$this->addCell($cell);
}
return $this;
}
/**
* @param int $cellIndex
*
* @return Row
*/
public function setCellAtIndex(Cell $cell, $cellIndex)
{
$this->cells[$cellIndex] = $cell;
return $this;
}
/**
* @param int $cellIndex
*
* @return null|Cell
*/
public function getCellAtIndex($cellIndex)
{
return $this->cells[$cellIndex] ?? null;
}
/**
* @return Row
*/
public function addCell(Cell $cell)
{
$this->cells[] = $cell;
return $this;
}
/**
* @return int
*/
public function getNumCells()
{
// When using "setCellAtIndex", it's possible to
// have "$this->cells" contain holes.
if (empty($this->cells)) {
return 0;
}
return max(array_keys($this->cells)) + 1;
}
/**
* @return Style
*/
public function getStyle()
{
return $this->style;
}
/**
* @param null|Style $style
*
* @return Row
*/
public function setStyle($style)
{
$this->style = $style ?: new Style();
return $this;
}
/**
* @return array The row values, as array
*/
public function toArray()
{
return array_map(function (Cell $cell) {
return $cell->getValue();
}, $this->cells);
}
/**
* Set row height.
*
* @param string $height
*
* @return Row
*/
public function setHeight($height)
{
$this->height = $height;
return $this;
}
/**
* Returns row height.
*
* @return string
*/
public function getHeight()
{
return $this->height;
}
}

View File

@ -1,80 +0,0 @@
<?php
namespace OpenSpout\Common\Entity\Style;
class Border
{
public const LEFT = 'left';
public const RIGHT = 'right';
public const TOP = 'top';
public const BOTTOM = 'bottom';
public const STYLE_NONE = 'none';
public const STYLE_SOLID = 'solid';
public const STYLE_DASHED = 'dashed';
public const STYLE_DOTTED = 'dotted';
public const STYLE_DOUBLE = 'double';
public const WIDTH_THIN = 'thin';
public const WIDTH_MEDIUM = 'medium';
public const WIDTH_THICK = 'thick';
/** @var array A list of BorderPart objects for this border. */
private $parts = [];
public function __construct(array $borderParts = [])
{
$this->setParts($borderParts);
}
/**
* @param string $name The name of the border part
*
* @return null|BorderPart
*/
public function getPart($name)
{
return $this->hasPart($name) ? $this->parts[$name] : null;
}
/**
* @param string $name The name of the border part
*
* @return bool
*/
public function hasPart($name)
{
return isset($this->parts[$name]);
}
/**
* @return array
*/
public function getParts()
{
return $this->parts;
}
/**
* Set BorderParts.
*
* @param array $parts
*/
public function setParts($parts)
{
$this->parts = [];
foreach ($parts as $part) {
$this->addPart($part);
}
}
/**
* @return Border
*/
public function addPart(BorderPart $borderPart)
{
$this->parts[$borderPart->getName()] = $borderPart;
return $this;
}
}

View File

@ -1,181 +0,0 @@
<?php
namespace OpenSpout\Common\Entity\Style;
use OpenSpout\Writer\Exception\Border\InvalidNameException;
use OpenSpout\Writer\Exception\Border\InvalidStyleException;
use OpenSpout\Writer\Exception\Border\InvalidWidthException;
class BorderPart
{
/**
* @var string the style of this border part
*/
protected $style;
/**
* @var string the name of this border part
*/
protected $name;
/**
* @var string the color of this border part
*/
protected $color;
/**
* @var string the width of this border part
*/
protected $width;
/**
* @var array allowed style constants for parts
*/
protected static $allowedStyles = [
'none',
'solid',
'dashed',
'dotted',
'double',
];
/**
* @var array allowed names constants for border parts
*/
protected static $allowedNames = [
'left',
'right',
'top',
'bottom',
];
/**
* @var array allowed width constants for border parts
*/
protected static $allowedWidths = [
'thin',
'medium',
'thick',
];
/**
* @param string $name @see BorderPart::$allowedNames
* @param string $color A RGB color code
* @param string $width @see BorderPart::$allowedWidths
* @param string $style @see BorderPart::$allowedStyles
*
* @throws InvalidNameException
* @throws InvalidStyleException
* @throws InvalidWidthException
*/
public function __construct($name, $color = Color::BLACK, $width = Border::WIDTH_MEDIUM, $style = Border::STYLE_SOLID)
{
$this->setName($name);
$this->setColor($color);
$this->setWidth($width);
$this->setStyle($style);
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @param string $name The name of the border part @see BorderPart::$allowedNames
*
* @throws InvalidNameException
*/
public function setName($name)
{
if (!\in_array($name, self::$allowedNames, true)) {
throw new InvalidNameException($name);
}
$this->name = $name;
}
/**
* @return string
*/
public function getStyle()
{
return $this->style;
}
/**
* @param string $style The style of the border part @see BorderPart::$allowedStyles
*
* @throws InvalidStyleException
*/
public function setStyle($style)
{
if (!\in_array($style, self::$allowedStyles, true)) {
throw new InvalidStyleException($style);
}
$this->style = $style;
}
/**
* @return string
*/
public function getColor()
{
return $this->color;
}
/**
* @param string $color The color of the border part @see Color::rgb()
*/
public function setColor($color)
{
$this->color = $color;
}
/**
* @return string
*/
public function getWidth()
{
return $this->width;
}
/**
* @param string $width The width of the border part @see BorderPart::$allowedWidths
*
* @throws InvalidWidthException
*/
public function setWidth($width)
{
if (!\in_array($width, self::$allowedWidths, true)) {
throw new InvalidWidthException($width);
}
$this->width = $width;
}
/**
* @return array
*/
public static function getAllowedStyles()
{
return self::$allowedStyles;
}
/**
* @return array
*/
public static function getAllowedNames()
{
return self::$allowedNames;
}
/**
* @return array
*/
public static function getAllowedWidths()
{
return self::$allowedWidths;
}
}

View File

@ -1,7 +0,0 @@
<?php
namespace OpenSpout\Common\Exception;
class EncodingConversionException extends SpoutException
{
}

View File

@ -1,7 +0,0 @@
<?php
namespace OpenSpout\Common\Exception;
class IOException extends SpoutException
{
}

View File

@ -1,7 +0,0 @@
<?php
namespace OpenSpout\Common\Exception;
class InvalidArgumentException extends SpoutException
{
}

View File

@ -1,7 +0,0 @@
<?php
namespace OpenSpout\Common\Exception;
class InvalidColorException extends SpoutException
{
}

View File

@ -1,7 +0,0 @@
<?php
namespace OpenSpout\Common\Exception;
abstract class SpoutException extends \Exception
{
}

View File

@ -1,7 +0,0 @@
<?php
namespace OpenSpout\Common\Exception;
class UnsupportedTypeException extends SpoutException
{
}

View File

@ -1,82 +0,0 @@
<?php
namespace OpenSpout\Common\Helper;
/**
* This class provides helper functions to determine the type of the cell value.
*/
class CellTypeHelper
{
/**
* @param null|mixed $value
*
* @return bool Whether the given value is considered "empty"
*/
public static function isEmpty($value)
{
return null === $value || '' === $value;
}
/**
* @param mixed $value
*
* @return bool Whether the given value is a non empty string
*/
public static function isNonEmptyString($value)
{
return 'string' === \gettype($value) && '' !== $value;
}
/**
* Returns whether the given value is numeric.
* A numeric value is from type "integer" or "double" ("float" is not returned by gettype).
*
* @param mixed $value
*
* @return bool Whether the given value is numeric
*/
public static function isNumeric($value)
{
$valueType = \gettype($value);
return 'integer' === $valueType || 'double' === $valueType;
}
/**
* Returns whether the given value is boolean.
* "true"/"false" and 0/1 are not booleans.
*
* @param mixed $value
*
* @return bool Whether the given value is boolean
*/
public static function isBoolean($value)
{
return 'boolean' === \gettype($value);
}
/**
* Returns whether the given value is a DateTime or DateInterval object.
*
* @param mixed $value
*
* @return bool Whether the given value is a DateTime or DateInterval object
*/
public static function isDateTimeOrDateInterval($value)
{
return
$value instanceof \DateTimeInterface
|| $value instanceof \DateInterval
;
}
/**
* @param mixed $value
*
* @return bool
*/
public static function isFormula($value)
{
return \is_string($value) && isset($value[0]) && '=' === $value[0];
}
}

View File

@ -1,37 +0,0 @@
<?php
namespace OpenSpout\Common\Helper\Escaper;
/**
* Provides functions to escape and unescape data for CSV files.
*/
class CSV implements EscaperInterface
{
/**
* Escapes the given string to make it compatible with CSV.
*
* @codeCoverageIgnore
*
* @param string $string The string to escape
*
* @return string The escaped string
*/
public function escape($string)
{
return $string;
}
/**
* Unescapes the given string to make it compatible with CSV.
*
* @codeCoverageIgnore
*
* @param string $string The string to unescape
*
* @return string The unescaped string
*/
public function unescape($string)
{
return $string;
}
}

View File

@ -1,63 +0,0 @@
<?php
namespace OpenSpout\Common\Helper\Escaper;
/**
* Provides functions to escape and unescape data for ODS files.
*/
class ODS implements EscaperInterface
{
/**
* Escapes the given string to make it compatible with XLSX.
*
* @param string $string The string to escape
*
* @return string The escaped string
*/
public function escape($string)
{
// @NOTE: Using ENT_QUOTES as XML entities ('<', '>', '&') as well as
// single/double quotes (for XML attributes) need to be encoded.
if (\defined('ENT_DISALLOWED')) {
/**
* 'ENT_DISALLOWED' ensures that invalid characters in the given document type are replaced.
* Otherwise control characters like a vertical tab "\v" will make the XML document unreadable by the XML processor.
*
* @see https://github.com/box/spout/issues/329
*/
$replacedString = htmlspecialchars($string, ENT_QUOTES | ENT_DISALLOWED, 'UTF-8');
} else {
// We are on hhvm or any other engine that does not support ENT_DISALLOWED.
$escapedString = htmlspecialchars($string, ENT_QUOTES, 'UTF-8');
// control characters values are from 0 to 1F (hex values) in the ASCII table
// some characters should not be escaped though: "\t", "\r" and "\n".
$regexPattern = '[\x00-\x08'.
// skipping "\t" (0x9) and "\n" (0xA)
'\x0B-\x0C'.
// skipping "\r" (0xD)
'\x0E-\x1F]';
$replacedString = preg_replace("/{$regexPattern}/", '<27>', $escapedString);
}
return $replacedString;
}
/**
* Unescapes the given string to make it compatible with XLSX.
*
* @param string $string The string to unescape
*
* @return string The unescaped string
*/
public function unescape($string)
{
// ==============
// = WARNING =
// ==============
// It is assumed that the given string has already had its XML entities decoded.
// This is true if the string is coming from a DOMNode (as DOMNode already decode XML entities on creation).
// Therefore there is no need to call "htmlspecialchars_decode()".
return $string;
}
}

View File

@ -1,371 +0,0 @@
<?php
namespace OpenSpout\Common\Helper;
/**
* This class wraps global functions to facilitate testing.
*
* @codeCoverageIgnore
*/
class GlobalFunctionsHelper
{
/**
* Wrapper around global function fopen().
*
* @see fopen()
*
* @param string $fileName
* @param string $mode
*
* @return bool|resource
*/
public function fopen($fileName, $mode)
{
return fopen($fileName, $mode);
}
/**
* Wrapper around global function fgets().
*
* @see fgets()
*
* @param resource $handle
* @param null|int $length
*
* @return string
*/
public function fgets($handle, $length = null)
{
return fgets($handle, $length);
}
/**
* Wrapper around global function fputs().
*
* @see fputs()
*
* @param resource $handle
* @param string $string
*
* @return int
*/
public function fputs($handle, $string)
{
return fwrite($handle, $string);
}
/**
* Wrapper around global function fflush().
*
* @see fflush()
*
* @param resource $handle
*
* @return bool
*/
public function fflush($handle)
{
return fflush($handle);
}
/**
* Wrapper around global function fseek().
*
* @see fseek()
*
* @param resource $handle
* @param int $offset
*
* @return int
*/
public function fseek($handle, $offset)
{
return fseek($handle, $offset);
}
/**
* Wrapper around global function fgetcsv().
*
* @see fgetcsv()
*
* @param resource $handle
* @param null|int $length
* @param null|string $delimiter
* @param null|string $enclosure
*
* @return array|false
*/
public function fgetcsv($handle, $length = null, $delimiter = null, $enclosure = null)
{
/**
* PHP uses '\' as the default escape character. This is not RFC-4180 compliant...
* To fix that, simply disable the escape character.
*
* @see https://bugs.php.net/bug.php?id=43225
* @see http://tools.ietf.org/html/rfc4180
*/
$escapeCharacter = \PHP_VERSION_ID >= 70400 ? '' : "\0";
return fgetcsv($handle, $length, $delimiter, $enclosure, $escapeCharacter);
}
/**
* Wrapper around global function fputcsv().
*
* @see fputcsv()
*
* @param resource $handle
* @param null|string $delimiter
* @param null|string $enclosure
*
* @return false|int
*/
public function fputcsv($handle, array $fields, $delimiter = null, $enclosure = null)
{
/**
* PHP uses '\' as the default escape character. This is not RFC-4180 compliant...
* To fix that, simply disable the escape character.
*
* @see https://bugs.php.net/bug.php?id=43225
* @see http://tools.ietf.org/html/rfc4180
*/
$escapeCharacter = \PHP_VERSION_ID >= 70400 ? '' : "\0";
return fputcsv($handle, $fields, $delimiter, $enclosure, $escapeCharacter);
}
/**
* Wrapper around global function fwrite().
*
* @see fwrite()
*
* @param resource $handle
* @param string $string
*
* @return int
*/
public function fwrite($handle, $string)
{
return fwrite($handle, $string);
}
/**
* Wrapper around global function fclose().
*
* @see fclose()
*
* @param resource $handle
*
* @return bool
*/
public function fclose($handle)
{
return fclose($handle);
}
/**
* Wrapper around global function rewind().
*
* @see rewind()
*
* @param resource $handle
*
* @return bool
*/
public function rewind($handle)
{
return rewind($handle);
}
/**
* Wrapper around global function file_exists().
*
* @see file_exists()
*
* @param string $fileName
*
* @return bool
*/
public function file_exists($fileName)
{
return file_exists($fileName);
}
/**
* Wrapper around global function file_get_contents().
*
* @see file_get_contents()
*
* @param string $filePath
*
* @return string
*/
public function file_get_contents($filePath)
{
$realFilePath = $this->convertToUseRealPath($filePath);
return file_get_contents($realFilePath);
}
/**
* Wrapper around global function feof().
*
* @see feof()
*
* @param resource $handle
*
* @return bool
*/
public function feof($handle)
{
return feof($handle);
}
/**
* Wrapper around global function is_readable().
*
* @see is_readable()
*
* @param string $fileName
*
* @return bool
*/
public function is_readable($fileName)
{
return is_readable($fileName);
}
/**
* Wrapper around global function basename().
*
* @see basename()
*
* @param string $path
* @param string $suffix
*
* @return string
*/
public function basename($path, $suffix = '')
{
return basename($path, $suffix);
}
/**
* Wrapper around global function header().
*
* @see header()
*
* @param string $string
*/
public function header($string)
{
header($string);
}
/**
* Wrapper around global function ob_end_clean().
*
* @see ob_end_clean()
*/
public function ob_end_clean()
{
if (ob_get_length() > 0) {
ob_end_clean();
}
}
/**
* Wrapper around global function iconv().
*
* @see iconv()
*
* @param string $string The string to be converted
* @param string $sourceEncoding The encoding of the source string
* @param string $targetEncoding The encoding the source string should be converted to
*
* @return bool|string the converted string or FALSE on failure
*/
public function iconv($string, $sourceEncoding, $targetEncoding)
{
return iconv($sourceEncoding, $targetEncoding, $string);
}
/**
* Wrapper around global function mb_convert_encoding().
*
* @see mb_convert_encoding()
*
* @param string $string The string to be converted
* @param string $sourceEncoding The encoding of the source string
* @param string $targetEncoding The encoding the source string should be converted to
*
* @return bool|string the converted string or FALSE on failure
*/
public function mb_convert_encoding($string, $sourceEncoding, $targetEncoding)
{
return mb_convert_encoding($string, $targetEncoding, $sourceEncoding);
}
/**
* Wrapper around global function stream_get_wrappers().
*
* @see stream_get_wrappers()
*
* @return array
*/
public function stream_get_wrappers()
{
return stream_get_wrappers();
}
/**
* Wrapper around global function function_exists().
*
* @see function_exists()
*
* @param string $functionName
*
* @return bool
*/
public function function_exists($functionName)
{
return \function_exists($functionName);
}
/**
* Updates the given file path to use a real path.
* This is to avoid issues on some Windows setup.
*
* @param string $filePath File path
*
* @return string The file path using a real path
*/
protected function convertToUseRealPath($filePath)
{
$realFilePath = $filePath;
if ($this->isZipStream($filePath)) {
if (preg_match('/zip:\/\/(.*)#(.*)/', $filePath, $matches)) {
$documentPath = $matches[1];
$documentInsideZipPath = $matches[2];
$realFilePath = 'zip://'.realpath($documentPath).'#'.$documentInsideZipPath;
}
} else {
$realFilePath = realpath($filePath);
}
return $realFilePath;
}
/**
* Returns whether the given path is a zip stream.
*
* @param string $path Path pointing to a document
*
* @return bool TRUE if path is a zip stream, FALSE otherwise
*/
protected function isZipStream($path)
{
return 0 === strpos($path, 'zip://');
}
}

View File

@ -1,108 +0,0 @@
<?php
namespace OpenSpout\Common\Helper;
/**
* This class provides helper functions to work with strings and multibyte strings.
*
* @codeCoverageIgnore
*/
class StringHelper
{
/** @var bool Whether the mbstring extension is loaded */
protected $hasMbstringSupport;
/** @var bool Whether the code is running with PHP7 or older versions */
private $isRunningPhp7OrOlder;
/** @var array Locale info, used for number formatting */
private $localeInfo;
public function __construct()
{
$this->hasMbstringSupport = \extension_loaded('mbstring');
$this->isRunningPhp7OrOlder = version_compare(PHP_VERSION, '8.0.0') < 0;
$this->localeInfo = localeconv();
}
/**
* Returns the length of the given string.
* It uses the multi-bytes function is available.
*
* @see strlen
* @see mb_strlen
*
* @param string $string
*
* @return int
*/
public function getStringLength($string)
{
return $this->hasMbstringSupport ? mb_strlen($string) : \strlen($string);
}
/**
* Returns the position of the first occurrence of the given character/substring within the given string.
* It uses the multi-bytes function is available.
*
* @see strpos
* @see mb_strpos
*
* @param string $char Needle
* @param string $string Haystack
*
* @return int Char/substring's first occurrence position within the string if found (starts at 0) or -1 if not found
*/
public function getCharFirstOccurrencePosition($char, $string)
{
$position = $this->hasMbstringSupport ? mb_strpos($string, $char) : strpos($string, $char);
return (false !== $position) ? $position : -1;
}
/**
* Returns the position of the last occurrence of the given character/substring within the given string.
* It uses the multi-bytes function is available.
*
* @see strrpos
* @see mb_strrpos
*
* @param string $char Needle
* @param string $string Haystack
*
* @return int Char/substring's last occurrence position within the string if found (starts at 0) or -1 if not found
*/
public function getCharLastOccurrencePosition($char, $string)
{
$position = $this->hasMbstringSupport ? mb_strrpos($string, $char) : strrpos($string, $char);
return (false !== $position) ? $position : -1;
}
/**
* Formats a numeric value (int or float) in a way that's compatible with the expected spreadsheet format.
*
* Formatting of float values is locale dependent in PHP < 8.
* Thousands separators and decimal points vary from locale to locale (en_US: 12.34 vs pl_PL: 12,34).
* However, float values must be formatted with no thousands separator and a "." as decimal point
* to work properly. This method can be used to convert the value to the correct format before storing it.
*
* @see https://wiki.php.net/rfc/locale_independent_float_to_string for the changed behavior in PHP8.
*
* @param float|int $numericValue
*
* @return float|int|string
*/
public function formatNumericValue($numericValue)
{
if ($this->isRunningPhp7OrOlder && \is_float($numericValue)) {
return str_replace(
[$this->localeInfo['thousands_sep'], $this->localeInfo['decimal_point']],
['', '.'],
(string) $numericValue
);
}
return $numericValue;
}
}

View File

@ -1,82 +0,0 @@
<?php
namespace OpenSpout\Common\Manager;
abstract class OptionsManagerAbstract implements OptionsManagerInterface
{
public const PREFIX_OPTION = 'OPTION_';
/** @var string[] List of all supported option names */
private $supportedOptions = [];
/** @var array Associative array [OPTION_NAME => OPTION_VALUE] */
private $options = [];
/**
* OptionsManagerAbstract constructor.
*/
public function __construct()
{
$this->supportedOptions = $this->getSupportedOptions();
$this->setDefaultOptions();
}
/**
* Sets the given option, if this option is supported.
*
* @param string $optionName
* @param mixed $optionValue
*/
public function setOption($optionName, $optionValue)
{
if (\in_array($optionName, $this->supportedOptions, true)) {
$this->options[$optionName] = $optionValue;
}
}
/**
* Add an option to the internal list of options
* Used only for mergeCells() for now.
*
* @param mixed $optionName
* @param mixed $optionValue
*/
public function addOption($optionName, $optionValue)
{
if (\in_array($optionName, $this->supportedOptions, true)) {
if (!isset($this->options[$optionName])) {
$this->options[$optionName] = [];
} elseif (!\is_array($this->options[$optionName])) {
$this->options[$optionName] = [$this->options[$optionName]];
}
$this->options[$optionName][] = $optionValue;
}
}
/**
* @param string $optionName
*
* @return null|mixed The set option or NULL if no option with given name found
*/
public function getOption($optionName)
{
$optionValue = null;
if (isset($this->options[$optionName])) {
$optionValue = $this->options[$optionName];
}
return $optionValue;
}
/**
* @return array List of supported options
*/
abstract protected function getSupportedOptions();
/**
* Sets the default options.
* To be overriden by child classes.
*/
abstract protected function setDefaultOptions();
}

View File

@ -1,31 +0,0 @@
<?php
namespace OpenSpout\Common\Manager;
/**
* Interface OptionsManagerInterface.
*/
interface OptionsManagerInterface
{
/**
* @param string $optionName
* @param mixed $optionValue
*/
public function setOption($optionName, $optionValue);
/**
* @param string $optionName
*
* @return null|mixed The set option or NULL if no option with given name found
*/
public function getOption($optionName);
/**
* Add an option to the internal list of options
* Used only for mergeCells() for now.
*
* @param mixed $optionName
* @param mixed $optionValue
*/
public function addOption($optionName, $optionValue);
}

View File

@ -1,13 +0,0 @@
<?php
namespace OpenSpout\Common;
/**
* This class references the supported types.
*/
abstract class Type
{
public const CSV = 'csv';
public const XLSX = 'xlsx';
public const ODS = 'ods';
}

View File

@ -1,98 +0,0 @@
<?php
namespace OpenSpout\Reader\CSV\Creator;
use OpenSpout\Common\Creator\HelperFactory;
use OpenSpout\Common\Entity\Cell;
use OpenSpout\Common\Entity\Row;
use OpenSpout\Common\Helper\GlobalFunctionsHelper;
use OpenSpout\Common\Manager\OptionsManagerInterface;
use OpenSpout\Reader\Common\Creator\InternalEntityFactoryInterface;
use OpenSpout\Reader\CSV\RowIterator;
use OpenSpout\Reader\CSV\Sheet;
use OpenSpout\Reader\CSV\SheetIterator;
/**
* Factory to create entities.
*/
class InternalEntityFactory implements InternalEntityFactoryInterface
{
/** @var HelperFactory */
private $helperFactory;
public function __construct(HelperFactory $helperFactory)
{
$this->helperFactory = $helperFactory;
}
/**
* @param resource $filePointer Pointer to the CSV file to read
* @param OptionsManagerInterface $optionsManager
* @param GlobalFunctionsHelper $globalFunctionsHelper
*
* @return SheetIterator
*/
public function createSheetIterator($filePointer, $optionsManager, $globalFunctionsHelper)
{
$rowIterator = $this->createRowIterator($filePointer, $optionsManager, $globalFunctionsHelper);
$sheet = $this->createSheet($rowIterator);
return new SheetIterator($sheet);
}
/**
* @param Cell[] $cells
*
* @return Row
*/
public function createRow(array $cells = [])
{
return new Row($cells, null);
}
/**
* @param mixed $cellValue
*
* @return Cell
*/
public function createCell($cellValue)
{
return new Cell($cellValue);
}
/**
* @return Row
*/
public function createRowFromArray(array $cellValues = [])
{
$cells = array_map(function ($cellValue) {
return $this->createCell($cellValue);
}, $cellValues);
return $this->createRow($cells);
}
/**
* @param RowIterator $rowIterator
*
* @return Sheet
*/
private function createSheet($rowIterator)
{
return new Sheet($rowIterator);
}
/**
* @param resource $filePointer Pointer to the CSV file to read
* @param OptionsManagerInterface $optionsManager
* @param GlobalFunctionsHelper $globalFunctionsHelper
*
* @return RowIterator
*/
private function createRowIterator($filePointer, $optionsManager, $globalFunctionsHelper)
{
$encodingHelper = $this->helperFactory->createEncodingHelper($globalFunctionsHelper);
return new RowIterator($filePointer, $optionsManager, $encodingHelper, $this, $globalFunctionsHelper);
}
}

View File

@ -1,39 +0,0 @@
<?php
namespace OpenSpout\Reader\CSV\Manager;
use OpenSpout\Common\Helper\EncodingHelper;
use OpenSpout\Common\Manager\OptionsManagerAbstract;
use OpenSpout\Reader\Common\Entity\Options;
/**
* CSV Reader options manager.
*/
class OptionsManager extends OptionsManagerAbstract
{
/**
* {@inheritdoc}
*/
protected function getSupportedOptions()
{
return [
Options::SHOULD_FORMAT_DATES,
Options::SHOULD_PRESERVE_EMPTY_ROWS,
Options::FIELD_DELIMITER,
Options::FIELD_ENCLOSURE,
Options::ENCODING,
];
}
/**
* {@inheritdoc}
*/
protected function setDefaultOptions()
{
$this->setOption(Options::SHOULD_FORMAT_DATES, false);
$this->setOption(Options::SHOULD_PRESERVE_EMPTY_ROWS, false);
$this->setOption(Options::FIELD_DELIMITER, ',');
$this->setOption(Options::FIELD_ENCLOSURE, '"');
$this->setOption(Options::ENCODING, EncodingHelper::ENCODING_UTF8);
}
}

View File

@ -1,149 +0,0 @@
<?php
namespace OpenSpout\Reader\CSV;
use OpenSpout\Common\Exception\IOException;
use OpenSpout\Common\Helper\GlobalFunctionsHelper;
use OpenSpout\Common\Manager\OptionsManagerInterface;
use OpenSpout\Reader\Common\Creator\InternalEntityFactoryInterface;
use OpenSpout\Reader\Common\Entity\Options;
use OpenSpout\Reader\CSV\Creator\InternalEntityFactory;
use OpenSpout\Reader\ReaderAbstract;
/**
* This class provides support to read data from a CSV file.
*/
class Reader extends ReaderAbstract
{
/** @var resource Pointer to the file to be written */
protected $filePointer;
/** @var SheetIterator To iterator over the CSV unique "sheet" */
protected $sheetIterator;
/** @var string Original value for the "auto_detect_line_endings" INI value */
protected $originalAutoDetectLineEndings;
/** @var bool Whether the code is running with PHP >= 8.1 */
private $isRunningAtLeastPhp81;
public function __construct(
OptionsManagerInterface $optionsManager,
GlobalFunctionsHelper $globalFunctionsHelper,
InternalEntityFactoryInterface $entityFactory
) {
parent::__construct($optionsManager, $globalFunctionsHelper, $entityFactory);
$this->isRunningAtLeastPhp81 = version_compare(PHP_VERSION, '8.1.0') >= 0;
}
/**
* Sets the field delimiter for the CSV.
* Needs to be called before opening the reader.
*
* @param string $fieldDelimiter Character that delimits fields
*
* @return Reader
*/
public function setFieldDelimiter($fieldDelimiter)
{
$this->optionsManager->setOption(Options::FIELD_DELIMITER, $fieldDelimiter);
return $this;
}
/**
* Sets the field enclosure for the CSV.
* Needs to be called before opening the reader.
*
* @param string $fieldEnclosure Character that enclose fields
*
* @return Reader
*/
public function setFieldEnclosure($fieldEnclosure)
{
$this->optionsManager->setOption(Options::FIELD_ENCLOSURE, $fieldEnclosure);
return $this;
}
/**
* Sets the encoding of the CSV file to be read.
* Needs to be called before opening the reader.
*
* @param string $encoding Encoding of the CSV file to be read
*
* @return Reader
*/
public function setEncoding($encoding)
{
$this->optionsManager->setOption(Options::ENCODING, $encoding);
return $this;
}
/**
* Returns whether stream wrappers are supported.
*
* @return bool
*/
protected function doesSupportStreamWrapper()
{
return true;
}
/**
* Opens the file at the given path to make it ready to be read.
* If setEncoding() was not called, it assumes that the file is encoded in UTF-8.
*
* @param string $filePath Path of the CSV file to be read
*
* @throws \OpenSpout\Common\Exception\IOException
*/
protected function openReader($filePath)
{
// "auto_detect_line_endings" is deprecated in PHP 8.1
if (!$this->isRunningAtLeastPhp81) {
$this->originalAutoDetectLineEndings = ini_get('auto_detect_line_endings');
ini_set('auto_detect_line_endings', '1');
}
$this->filePointer = $this->globalFunctionsHelper->fopen($filePath, 'r');
if (!$this->filePointer) {
throw new IOException("Could not open file {$filePath} for reading.");
}
/** @var InternalEntityFactory $entityFactory */
$entityFactory = $this->entityFactory;
$this->sheetIterator = $entityFactory->createSheetIterator(
$this->filePointer,
$this->optionsManager,
$this->globalFunctionsHelper
);
}
/**
* Returns an iterator to iterate over sheets.
*
* @return SheetIterator To iterate over sheets
*/
protected function getConcreteSheetIterator()
{
return $this->sheetIterator;
}
/**
* Closes the reader. To be used after reading the file.
*/
protected function closeReader()
{
if (\is_resource($this->filePointer)) {
$this->globalFunctionsHelper->fclose($this->filePointer);
}
// "auto_detect_line_endings" is deprecated in PHP 8.1
if (!$this->isRunningAtLeastPhp81) {
ini_set('auto_detect_line_endings', $this->originalAutoDetectLineEndings);
}
}
}

View File

@ -1,26 +0,0 @@
<?php
namespace OpenSpout\Reader\Common\Creator;
use OpenSpout\Common\Entity\Cell;
use OpenSpout\Common\Entity\Row;
/**
* Interface EntityFactoryInterface.
*/
interface InternalEntityFactoryInterface
{
/**
* @param Cell[] $cells
*
* @return Row
*/
public function createRow(array $cells = []);
/**
* @param mixed $cellValue
*
* @return Cell
*/
public function createCell($cellValue);
}

View File

@ -1,72 +0,0 @@
<?php
namespace OpenSpout\Reader\Common\Creator;
use OpenSpout\Common\Exception\UnsupportedTypeException;
use OpenSpout\Common\Type;
use OpenSpout\Reader\ReaderInterface;
/**
* Factory to create external entities.
*/
class ReaderEntityFactory
{
/**
* Creates a reader by file extension.
*
* @param string $path The path to the spreadsheet file. Supported extensions are .csv, .ods and .xlsx
*
* @throws \OpenSpout\Common\Exception\UnsupportedTypeException
*
* @return ReaderInterface
*/
public static function createReaderFromFile(string $path)
{
return ReaderFactory::createFromFile($path);
}
/**
* This creates an instance of a CSV reader.
*
* @return \OpenSpout\Reader\CSV\Reader
*/
public static function createCSVReader()
{
try {
return ReaderFactory::createFromType(Type::CSV);
} catch (UnsupportedTypeException $e) {
// should never happen
return null;
}
}
/**
* This creates an instance of a XLSX reader.
*
* @return \OpenSpout\Reader\XLSX\Reader
*/
public static function createXLSXReader()
{
try {
return ReaderFactory::createFromType(Type::XLSX);
} catch (UnsupportedTypeException $e) {
// should never happen
return null;
}
}
/**
* This creates an instance of a ODS reader.
*
* @return \OpenSpout\Reader\ODS\Reader
*/
public static function createODSReader()
{
try {
return ReaderFactory::createFromType(Type::ODS);
} catch (UnsupportedTypeException $e) {
// should never happen
return null;
}
}
}

View File

@ -1,109 +0,0 @@
<?php
namespace OpenSpout\Reader\Common\Creator;
use OpenSpout\Common\Creator\HelperFactory;
use OpenSpout\Common\Exception\UnsupportedTypeException;
use OpenSpout\Common\Type;
use OpenSpout\Reader\CSV\Creator\InternalEntityFactory as CSVInternalEntityFactory;
use OpenSpout\Reader\CSV\Manager\OptionsManager as CSVOptionsManager;
use OpenSpout\Reader\CSV\Reader as CSVReader;
use OpenSpout\Reader\ODS\Creator\HelperFactory as ODSHelperFactory;
use OpenSpout\Reader\ODS\Creator\InternalEntityFactory as ODSInternalEntityFactory;
use OpenSpout\Reader\ODS\Creator\ManagerFactory as ODSManagerFactory;
use OpenSpout\Reader\ODS\Manager\OptionsManager as ODSOptionsManager;
use OpenSpout\Reader\ODS\Reader as ODSReader;
use OpenSpout\Reader\ReaderInterface;
use OpenSpout\Reader\XLSX\Creator\HelperFactory as XLSXHelperFactory;
use OpenSpout\Reader\XLSX\Creator\InternalEntityFactory as XLSXInternalEntityFactory;
use OpenSpout\Reader\XLSX\Creator\ManagerFactory as XLSXManagerFactory;
use OpenSpout\Reader\XLSX\Manager\OptionsManager as XLSXOptionsManager;
use OpenSpout\Reader\XLSX\Manager\SharedStringsCaching\CachingStrategyFactory;
use OpenSpout\Reader\XLSX\Reader as XLSXReader;
/**
* This factory is used to create readers, based on the type of the file to be read.
* It supports CSV, XLSX and ODS formats.
*/
class ReaderFactory
{
/**
* Creates a reader by file extension.
*
* @param string $path The path to the spreadsheet file. Supported extensions are .csv,.ods and .xlsx
*
* @throws \OpenSpout\Common\Exception\UnsupportedTypeException
*
* @return ReaderInterface
*/
public static function createFromFile(string $path)
{
$extension = strtolower(pathinfo($path, PATHINFO_EXTENSION));
return self::createFromType($extension);
}
/**
* This creates an instance of the appropriate reader, given the type of the file to be read.
*
* @param string $readerType Type of the reader to instantiate
*
* @throws \OpenSpout\Common\Exception\UnsupportedTypeException
*
* @return ReaderInterface
*/
public static function createFromType($readerType)
{
switch ($readerType) {
case Type::CSV: return self::createCSVReader();
case Type::XLSX: return self::createXLSXReader();
case Type::ODS: return self::createODSReader();
default:
throw new UnsupportedTypeException('No readers supporting the given type: '.$readerType);
}
}
/**
* @return CSVReader
*/
private static function createCSVReader()
{
$optionsManager = new CSVOptionsManager();
$helperFactory = new HelperFactory();
$entityFactory = new CSVInternalEntityFactory($helperFactory);
$globalFunctionsHelper = $helperFactory->createGlobalFunctionsHelper();
return new CSVReader($optionsManager, $globalFunctionsHelper, $entityFactory);
}
/**
* @return XLSXReader
*/
private static function createXLSXReader()
{
$optionsManager = new XLSXOptionsManager();
$helperFactory = new XLSXHelperFactory();
$managerFactory = new XLSXManagerFactory($helperFactory, new CachingStrategyFactory());
$entityFactory = new XLSXInternalEntityFactory($managerFactory, $helperFactory);
$globalFunctionsHelper = $helperFactory->createGlobalFunctionsHelper();
return new XLSXReader($optionsManager, $globalFunctionsHelper, $entityFactory, $managerFactory);
}
/**
* @return ODSReader
*/
private static function createODSReader()
{
$optionsManager = new ODSOptionsManager();
$helperFactory = new ODSHelperFactory();
$managerFactory = new ODSManagerFactory();
$entityFactory = new ODSInternalEntityFactory($helperFactory, $managerFactory);
$globalFunctionsHelper = $helperFactory->createGlobalFunctionsHelper();
return new ODSReader($optionsManager, $globalFunctionsHelper, $entityFactory);
}
}

View File

@ -1,22 +0,0 @@
<?php
namespace OpenSpout\Reader\Common\Entity;
/**
* Readers' options holder.
*/
abstract class Options
{
// Common options
public const SHOULD_FORMAT_DATES = 'shouldFormatDates';
public const SHOULD_PRESERVE_EMPTY_ROWS = 'shouldPreserveEmptyRows';
// CSV specific options
public const FIELD_DELIMITER = 'fieldDelimiter';
public const FIELD_ENCLOSURE = 'fieldEnclosure';
public const ENCODING = 'encoding';
// XLSX specific options
public const TEMP_FOLDER = 'tempFolder';
public const SHOULD_USE_1904_DATES = 'shouldUse1904Dates';
}

View File

@ -1,30 +0,0 @@
<?php
namespace OpenSpout\Reader\Exception;
use Throwable;
class InvalidValueException extends ReaderException
{
/** @var mixed */
private $invalidValue;
/**
* @param mixed $invalidValue
* @param string $message
* @param int $code
*/
public function __construct($invalidValue, $message = '', $code = 0, Throwable $previous = null)
{
$this->invalidValue = $invalidValue;
parent::__construct($message, $code, $previous);
}
/**
* @return mixed
*/
public function getInvalidValue()
{
return $this->invalidValue;
}
}

View File

@ -1,7 +0,0 @@
<?php
namespace OpenSpout\Reader\Exception;
class IteratorNotRewindableException extends ReaderException
{
}

View File

@ -1,7 +0,0 @@
<?php
namespace OpenSpout\Reader\Exception;
class NoSheetsFoundException extends ReaderException
{
}

View File

@ -1,9 +0,0 @@
<?php
namespace OpenSpout\Reader\Exception;
use OpenSpout\Common\Exception\SpoutException;
abstract class ReaderException extends SpoutException
{
}

View File

@ -1,7 +0,0 @@
<?php
namespace OpenSpout\Reader\Exception;
class ReaderNotOpenedException extends ReaderException
{
}

View File

@ -1,7 +0,0 @@
<?php
namespace OpenSpout\Reader\Exception;
class SharedStringNotFoundException extends ReaderException
{
}

View File

@ -1,7 +0,0 @@
<?php
namespace OpenSpout\Reader\Exception;
class XMLProcessingException extends ReaderException
{
}

View File

@ -1,14 +0,0 @@
<?php
namespace OpenSpout\Reader;
/**
* Interface IteratorInterface.
*/
interface IteratorInterface extends \Iterator
{
/**
* Cleans up what was created to iterate over the object.
*/
public function end();
}

View File

@ -1,43 +0,0 @@
<?php
namespace OpenSpout\Reader\ODS\Creator;
use OpenSpout\Reader\ODS\Helper\CellValueFormatter;
use OpenSpout\Reader\ODS\Helper\SettingsHelper;
/**
* Factory to create helpers.
*/
class HelperFactory extends \OpenSpout\Common\Creator\HelperFactory
{
/**
* @param bool $shouldFormatDates Whether date/time values should be returned as PHP objects or be formatted as strings
*
* @return CellValueFormatter
*/
public function createCellValueFormatter($shouldFormatDates)
{
$escaper = $this->createStringsEscaper();
return new CellValueFormatter($shouldFormatDates, $escaper);
}
/**
* @param InternalEntityFactory $entityFactory
*
* @return SettingsHelper
*/
public function createSettingsHelper($entityFactory)
{
return new SettingsHelper($entityFactory);
}
/**
* @return \OpenSpout\Common\Helper\Escaper\ODS
*/
public function createStringsEscaper()
{
// @noinspection PhpUnnecessaryFullyQualifiedNameInspection
return new \OpenSpout\Common\Helper\Escaper\ODS();
}
}

View File

@ -1,124 +0,0 @@
<?php
namespace OpenSpout\Reader\ODS\Creator;
use OpenSpout\Common\Entity\Cell;
use OpenSpout\Common\Entity\Row;
use OpenSpout\Reader\Common\Creator\InternalEntityFactoryInterface;
use OpenSpout\Reader\Common\Entity\Options;
use OpenSpout\Reader\Common\XMLProcessor;
use OpenSpout\Reader\ODS\RowIterator;
use OpenSpout\Reader\ODS\Sheet;
use OpenSpout\Reader\ODS\SheetIterator;
use OpenSpout\Reader\Wrapper\XMLReader;
/**
* Factory to create entities.
*/
class InternalEntityFactory implements InternalEntityFactoryInterface
{
/** @var HelperFactory */
private $helperFactory;
/** @var ManagerFactory */
private $managerFactory;
public function __construct(HelperFactory $helperFactory, ManagerFactory $managerFactory)
{
$this->helperFactory = $helperFactory;
$this->managerFactory = $managerFactory;
}
/**
* @param string $filePath Path of the file to be read
* @param \OpenSpout\Common\Manager\OptionsManagerInterface $optionsManager Reader's options manager
*
* @return SheetIterator
*/
public function createSheetIterator($filePath, $optionsManager)
{
$escaper = $this->helperFactory->createStringsEscaper();
$settingsHelper = $this->helperFactory->createSettingsHelper($this);
return new SheetIterator($filePath, $optionsManager, $escaper, $settingsHelper, $this);
}
/**
* @param XMLReader $xmlReader XML Reader
* @param int $sheetIndex Index of the sheet, based on order in the workbook (zero-based)
* @param string $sheetName Name of the sheet
* @param bool $isSheetActive Whether the sheet was defined as active
* @param bool $isSheetVisible Whether the sheet is visible
* @param \OpenSpout\Common\Manager\OptionsManagerInterface $optionsManager Reader's options manager
*
* @return Sheet
*/
public function createSheet($xmlReader, $sheetIndex, $sheetName, $isSheetActive, $isSheetVisible, $optionsManager)
{
$rowIterator = $this->createRowIterator($xmlReader, $optionsManager);
return new Sheet($rowIterator, $sheetIndex, $sheetName, $isSheetActive, $isSheetVisible);
}
/**
* @param Cell[] $cells
*
* @return Row
*/
public function createRow(array $cells = [])
{
return new Row($cells, null);
}
/**
* @param mixed $cellValue
*
* @return Cell
*/
public function createCell($cellValue)
{
return new Cell($cellValue);
}
/**
* @return XMLReader
*/
public function createXMLReader()
{
return new XMLReader();
}
/**
* @return \ZipArchive
*/
public function createZipArchive()
{
return new \ZipArchive();
}
/**
* @param XMLReader $xmlReader XML Reader
* @param \OpenSpout\Common\Manager\OptionsManagerInterface $optionsManager Reader's options manager
*
* @return RowIterator
*/
private function createRowIterator($xmlReader, $optionsManager)
{
$shouldFormatDates = $optionsManager->getOption(Options::SHOULD_FORMAT_DATES);
$cellValueFormatter = $this->helperFactory->createCellValueFormatter($shouldFormatDates);
$xmlProcessor = $this->createXMLProcessor($xmlReader);
$rowManager = $this->managerFactory->createRowManager($this);
return new RowIterator($xmlReader, $optionsManager, $cellValueFormatter, $xmlProcessor, $rowManager, $this);
}
/**
* @param XMLReader $xmlReader
*
* @return XMLProcessor
*/
private function createXMLProcessor($xmlReader)
{
return new XMLProcessor($xmlReader);
}
}

View File

@ -1,21 +0,0 @@
<?php
namespace OpenSpout\Reader\ODS\Creator;
use OpenSpout\Reader\Common\Manager\RowManager;
/**
* Factory to create managers.
*/
class ManagerFactory
{
/**
* @param InternalEntityFactory $entityFactory Factory to create entities
*
* @return RowManager
*/
public function createRowManager($entityFactory)
{
return new RowManager($entityFactory);
}
}

View File

@ -1,32 +0,0 @@
<?php
namespace OpenSpout\Reader\ODS\Manager;
use OpenSpout\Common\Manager\OptionsManagerAbstract;
use OpenSpout\Reader\Common\Entity\Options;
/**
* ODS Reader options manager.
*/
class OptionsManager extends OptionsManagerAbstract
{
/**
* {@inheritdoc}
*/
protected function getSupportedOptions()
{
return [
Options::SHOULD_FORMAT_DATES,
Options::SHOULD_PRESERVE_EMPTY_ROWS,
];
}
/**
* {@inheritdoc}
*/
protected function setDefaultOptions()
{
$this->setOption(Options::SHOULD_FORMAT_DATES, false);
$this->setOption(Options::SHOULD_PRESERVE_EMPTY_ROWS, false);
}
}

View File

@ -1,73 +0,0 @@
<?php
namespace OpenSpout\Reader\ODS;
use OpenSpout\Common\Exception\IOException;
use OpenSpout\Reader\ODS\Creator\InternalEntityFactory;
use OpenSpout\Reader\ReaderAbstract;
/**
* This class provides support to read data from a ODS file.
*/
class Reader extends ReaderAbstract
{
/** @var \ZipArchive */
protected $zip;
/** @var SheetIterator To iterator over the ODS sheets */
protected $sheetIterator;
/**
* Returns whether stream wrappers are supported.
*
* @return bool
*/
protected function doesSupportStreamWrapper()
{
return false;
}
/**
* Opens the file at the given file path to make it ready to be read.
*
* @param string $filePath Path of the file to be read
*
* @throws \OpenSpout\Common\Exception\IOException If the file at the given path or its content cannot be read
* @throws \OpenSpout\Reader\Exception\NoSheetsFoundException If there are no sheets in the file
*/
protected function openReader($filePath)
{
/** @var InternalEntityFactory $entityFactory */
$entityFactory = $this->entityFactory;
$this->zip = $entityFactory->createZipArchive();
if (true === $this->zip->open($filePath)) {
/** @var InternalEntityFactory $entityFactory */
$entityFactory = $this->entityFactory;
$this->sheetIterator = $entityFactory->createSheetIterator($filePath, $this->optionsManager);
} else {
throw new IOException("Could not open {$filePath} for reading.");
}
}
/**
* Returns an iterator to iterate over sheets.
*
* @return SheetIterator To iterate over sheets
*/
protected function getConcreteSheetIterator()
{
return $this->sheetIterator;
}
/**
* Closes the reader. To be used after reading the file.
*/
protected function closeReader()
{
if (null !== $this->zip) {
$this->zip->close();
}
}
}

View File

@ -1,22 +0,0 @@
<?php
declare(strict_types=1);
namespace OpenSpout\Reader;
use OpenSpout\Common\Entity\Row;
interface RowIteratorInterface extends IteratorInterface
{
/**
* Cleans up what was created to iterate over the object.
*/
#[\ReturnTypeWillChange]
public function end();
/**
* @return null|Row
*/
#[\ReturnTypeWillChange]
public function current();
}

View File

@ -1,34 +0,0 @@
<?php
namespace OpenSpout\Reader;
/**
* Interface SheetInterface.
*/
interface SheetInterface
{
/**
* @return IteratorInterface iterator to iterate over the sheet's rows
*/
public function getRowIterator();
/**
* @return int Index of the sheet
*/
public function getIndex();
/**
* @return string Name of the sheet
*/
public function getName();
/**
* @return bool Whether the sheet was defined as active
*/
public function isActive();
/**
* @return bool Whether the sheet is visible
*/
public function isVisible();
}

View File

@ -1,23 +0,0 @@
<?php
declare(strict_types=1);
namespace OpenSpout\Reader;
/**
* Interface IteratorInterface.
*/
interface SheetIteratorInterface extends IteratorInterface
{
/**
* Cleans up what was created to iterate over the object.
*/
#[\ReturnTypeWillChange]
public function end();
/**
* @return null|SheetInterface
*/
#[\ReturnTypeWillChange]
public function current();
}

View File

@ -1,38 +0,0 @@
<?php
namespace OpenSpout\Reader\XLSX\Creator;
use OpenSpout\Common\Helper\Escaper;
use OpenSpout\Reader\XLSX\Helper\CellValueFormatter;
use OpenSpout\Reader\XLSX\Manager\SharedStringsManager;
use OpenSpout\Reader\XLSX\Manager\StyleManager;
/**
* Factory to create helpers.
*/
class HelperFactory extends \OpenSpout\Common\Creator\HelperFactory
{
/**
* @param SharedStringsManager $sharedStringsManager Manages shared strings
* @param StyleManager $styleManager Manages styles
* @param bool $shouldFormatDates Whether date/time values should be returned as PHP objects or be formatted as strings
* @param bool $shouldUse1904Dates Whether date/time values should use a calendar starting in 1904 instead of 1900
*
* @return CellValueFormatter
*/
public function createCellValueFormatter($sharedStringsManager, $styleManager, $shouldFormatDates, $shouldUse1904Dates)
{
$escaper = $this->createStringsEscaper();
return new CellValueFormatter($sharedStringsManager, $styleManager, $shouldFormatDates, $shouldUse1904Dates, $escaper);
}
/**
* @return Escaper\XLSX
*/
public function createStringsEscaper()
{
// @noinspection PhpUnnecessaryFullyQualifiedNameInspection
return new Escaper\XLSX();
}
}

View File

@ -1,163 +0,0 @@
<?php
namespace OpenSpout\Reader\XLSX\Creator;
use OpenSpout\Common\Entity\Cell;
use OpenSpout\Common\Entity\Row;
use OpenSpout\Reader\Common\Creator\InternalEntityFactoryInterface;
use OpenSpout\Reader\Common\Entity\Options;
use OpenSpout\Reader\Common\XMLProcessor;
use OpenSpout\Reader\Wrapper\XMLReader;
use OpenSpout\Reader\XLSX\Manager\SharedStringsManager;
use OpenSpout\Reader\XLSX\RowIterator;
use OpenSpout\Reader\XLSX\Sheet;
use OpenSpout\Reader\XLSX\SheetIterator;
/**
* Factory to create entities.
*/
class InternalEntityFactory implements InternalEntityFactoryInterface
{
/** @var HelperFactory */
private $helperFactory;
/** @var ManagerFactory */
private $managerFactory;
public function __construct(ManagerFactory $managerFactory, HelperFactory $helperFactory)
{
$this->managerFactory = $managerFactory;
$this->helperFactory = $helperFactory;
}
/**
* @param string $filePath Path of the file to be read
* @param \OpenSpout\Common\Manager\OptionsManagerInterface $optionsManager Reader's options manager
* @param SharedStringsManager $sharedStringsManager Manages shared strings
*
* @return SheetIterator
*/
public function createSheetIterator($filePath, $optionsManager, $sharedStringsManager)
{
$sheetManager = $this->managerFactory->createSheetManager(
$filePath,
$optionsManager,
$sharedStringsManager,
$this
);
return new SheetIterator($sheetManager);
}
/**
* @param string $filePath Path of the XLSX file being read
* @param string $sheetDataXMLFilePath Path of the sheet data XML file as in [Content_Types].xml
* @param int $sheetIndex Index of the sheet, based on order in the workbook (zero-based)
* @param string $sheetName Name of the sheet
* @param bool $isSheetActive Whether the sheet was defined as active
* @param bool $isSheetVisible Whether the sheet is visible
* @param \OpenSpout\Common\Manager\OptionsManagerInterface $optionsManager Reader's options manager
* @param SharedStringsManager $sharedStringsManager Manages shared strings
*
* @return Sheet
*/
public function createSheet(
$filePath,
$sheetDataXMLFilePath,
$sheetIndex,
$sheetName,
$isSheetActive,
$isSheetVisible,
$optionsManager,
$sharedStringsManager
) {
$rowIterator = $this->createRowIterator($filePath, $sheetDataXMLFilePath, $optionsManager, $sharedStringsManager);
return new Sheet($rowIterator, $sheetIndex, $sheetName, $isSheetActive, $isSheetVisible);
}
/**
* @param Cell[] $cells
*
* @return Row
*/
public function createRow(array $cells = [])
{
return new Row($cells, null);
}
/**
* @param mixed $cellValue
*
* @return Cell
*/
public function createCell($cellValue)
{
return new Cell($cellValue);
}
/**
* @return \ZipArchive
*/
public function createZipArchive()
{
return new \ZipArchive();
}
/**
* @return XMLReader
*/
public function createXMLReader()
{
return new XMLReader();
}
/**
* @param XMLReader $xmlReader
*
* @return XMLProcessor
*/
public function createXMLProcessor($xmlReader)
{
return new XMLProcessor($xmlReader);
}
/**
* @param string $filePath Path of the XLSX file being read
* @param string $sheetDataXMLFilePath Path of the sheet data XML file as in [Content_Types].xml
* @param \OpenSpout\Common\Manager\OptionsManagerInterface $optionsManager Reader's options manager
* @param SharedStringsManager $sharedStringsManager Manages shared strings
*
* @return RowIterator
*/
private function createRowIterator($filePath, $sheetDataXMLFilePath, $optionsManager, $sharedStringsManager)
{
$xmlReader = $this->createXMLReader();
$xmlProcessor = $this->createXMLProcessor($xmlReader);
$styleManager = $this->managerFactory->createStyleManager($filePath, $this);
$rowManager = $this->managerFactory->createRowManager($this);
$shouldFormatDates = $optionsManager->getOption(Options::SHOULD_FORMAT_DATES);
$shouldUse1904Dates = $optionsManager->getOption(Options::SHOULD_USE_1904_DATES);
$cellValueFormatter = $this->helperFactory->createCellValueFormatter(
$sharedStringsManager,
$styleManager,
$shouldFormatDates,
$shouldUse1904Dates
);
$shouldPreserveEmptyRows = $optionsManager->getOption(Options::SHOULD_PRESERVE_EMPTY_ROWS);
return new RowIterator(
$filePath,
$sheetDataXMLFilePath,
$shouldPreserveEmptyRows,
$xmlReader,
$xmlProcessor,
$cellValueFormatter,
$rowManager,
$this
);
}
}

View File

@ -1,109 +0,0 @@
<?php
namespace OpenSpout\Reader\XLSX\Creator;
use OpenSpout\Reader\Common\Manager\RowManager;
use OpenSpout\Reader\XLSX\Manager\SharedStringsCaching\CachingStrategyFactory;
use OpenSpout\Reader\XLSX\Manager\SharedStringsManager;
use OpenSpout\Reader\XLSX\Manager\SheetManager;
use OpenSpout\Reader\XLSX\Manager\StyleManager;
use OpenSpout\Reader\XLSX\Manager\WorkbookRelationshipsManager;
/**
* Factory to create managers.
*/
class ManagerFactory
{
/** @var HelperFactory */
private $helperFactory;
/** @var CachingStrategyFactory */
private $cachingStrategyFactory;
/** @var null|WorkbookRelationshipsManager */
private $cachedWorkbookRelationshipsManager;
/**
* @param HelperFactory $helperFactory Factory to create helpers
* @param CachingStrategyFactory $cachingStrategyFactory Factory to create shared strings caching strategies
*/
public function __construct(HelperFactory $helperFactory, CachingStrategyFactory $cachingStrategyFactory)
{
$this->helperFactory = $helperFactory;
$this->cachingStrategyFactory = $cachingStrategyFactory;
}
/**
* @param string $filePath Path of the XLSX file being read
* @param string $tempFolder Temporary folder where the temporary files to store shared strings will be stored
* @param InternalEntityFactory $entityFactory Factory to create entities
*
* @return SharedStringsManager
*/
public function createSharedStringsManager($filePath, $tempFolder, $entityFactory)
{
$workbookRelationshipsManager = $this->createWorkbookRelationshipsManager($filePath, $entityFactory);
return new SharedStringsManager(
$filePath,
$tempFolder,
$workbookRelationshipsManager,
$entityFactory,
$this->helperFactory,
$this->cachingStrategyFactory
);
}
/**
* @param string $filePath Path of the XLSX file being read
* @param \OpenSpout\Common\Manager\OptionsManagerInterface $optionsManager Reader's options manager
* @param \OpenSpout\Reader\XLSX\Manager\SharedStringsManager $sharedStringsManager Manages shared strings
* @param InternalEntityFactory $entityFactory Factory to create entities
*
* @return SheetManager
*/
public function createSheetManager($filePath, $optionsManager, $sharedStringsManager, $entityFactory)
{
$escaper = $this->helperFactory->createStringsEscaper();
return new SheetManager($filePath, $optionsManager, $sharedStringsManager, $escaper, $entityFactory);
}
/**
* @param string $filePath Path of the XLSX file being read
* @param InternalEntityFactory $entityFactory Factory to create entities
*
* @return StyleManager
*/
public function createStyleManager($filePath, $entityFactory)
{
$workbookRelationshipsManager = $this->createWorkbookRelationshipsManager($filePath, $entityFactory);
return new StyleManager($filePath, $workbookRelationshipsManager, $entityFactory);
}
/**
* @param InternalEntityFactory $entityFactory Factory to create entities
*
* @return RowManager
*/
public function createRowManager($entityFactory)
{
return new RowManager($entityFactory);
}
/**
* @param string $filePath Path of the XLSX file being read
* @param InternalEntityFactory $entityFactory Factory to create entities
*
* @return WorkbookRelationshipsManager
*/
private function createWorkbookRelationshipsManager($filePath, $entityFactory)
{
if (!isset($this->cachedWorkbookRelationshipsManager)) {
$this->cachedWorkbookRelationshipsManager = new WorkbookRelationshipsManager($filePath, $entityFactory);
}
return $this->cachedWorkbookRelationshipsManager;
}
}

View File

@ -1,36 +0,0 @@
<?php
namespace OpenSpout\Reader\XLSX\Manager;
use OpenSpout\Common\Manager\OptionsManagerAbstract;
use OpenSpout\Reader\Common\Entity\Options;
/**
* XLSX Reader options manager.
*/
class OptionsManager extends OptionsManagerAbstract
{
/**
* {@inheritdoc}
*/
protected function getSupportedOptions()
{
return [
Options::TEMP_FOLDER,
Options::SHOULD_FORMAT_DATES,
Options::SHOULD_PRESERVE_EMPTY_ROWS,
Options::SHOULD_USE_1904_DATES,
];
}
/**
* {@inheritdoc}
*/
protected function setDefaultOptions()
{
$this->setOption(Options::TEMP_FOLDER, sys_get_temp_dir());
$this->setOption(Options::SHOULD_FORMAT_DATES, true);
$this->setOption(Options::SHOULD_PRESERVE_EMPTY_ROWS, false);
$this->setOption(Options::SHOULD_USE_1904_DATES, false);
}
}

View File

@ -1,122 +0,0 @@
<?php
namespace OpenSpout\Reader\XLSX;
use OpenSpout\Common\Exception\IOException;
use OpenSpout\Common\Helper\GlobalFunctionsHelper;
use OpenSpout\Common\Manager\OptionsManagerInterface;
use OpenSpout\Reader\Common\Creator\InternalEntityFactoryInterface;
use OpenSpout\Reader\Common\Entity\Options;
use OpenSpout\Reader\ReaderAbstract;
use OpenSpout\Reader\XLSX\Creator\InternalEntityFactory;
use OpenSpout\Reader\XLSX\Creator\ManagerFactory;
/**
* This class provides support to read data from a XLSX file.
*/
class Reader extends ReaderAbstract
{
/** @var ManagerFactory */
protected $managerFactory;
/** @var \ZipArchive */
protected $zip;
/** @var \OpenSpout\Reader\XLSX\Manager\SharedStringsManager Manages shared strings */
protected $sharedStringsManager;
/** @var SheetIterator To iterator over the XLSX sheets */
protected $sheetIterator;
public function __construct(
OptionsManagerInterface $optionsManager,
GlobalFunctionsHelper $globalFunctionsHelper,
InternalEntityFactoryInterface $entityFactory,
ManagerFactory $managerFactory
) {
parent::__construct($optionsManager, $globalFunctionsHelper, $entityFactory);
$this->managerFactory = $managerFactory;
}
/**
* @param string $tempFolder Temporary folder where the temporary files will be created
*
* @return Reader
*/
public function setTempFolder($tempFolder)
{
$this->optionsManager->setOption(Options::TEMP_FOLDER, $tempFolder);
return $this;
}
/**
* Returns whether stream wrappers are supported.
*
* @return bool
*/
protected function doesSupportStreamWrapper()
{
return false;
}
/**
* Opens the file at the given file path to make it ready to be read.
* It also parses the sharedStrings.xml file to get all the shared strings available in memory
* and fetches all the available sheets.
*
* @param string $filePath Path of the file to be read
*
* @throws \OpenSpout\Common\Exception\IOException If the file at the given path or its content cannot be read
* @throws \OpenSpout\Reader\Exception\NoSheetsFoundException If there are no sheets in the file
*/
protected function openReader($filePath)
{
/** @var InternalEntityFactory $entityFactory */
$entityFactory = $this->entityFactory;
$this->zip = $entityFactory->createZipArchive();
if (true === $this->zip->open($filePath)) {
$tempFolder = $this->optionsManager->getOption(Options::TEMP_FOLDER);
$this->sharedStringsManager = $this->managerFactory->createSharedStringsManager($filePath, $tempFolder, $entityFactory);
if ($this->sharedStringsManager->hasSharedStrings()) {
// Extracts all the strings from the sheets for easy access in the future
$this->sharedStringsManager->extractSharedStrings();
}
$this->sheetIterator = $entityFactory->createSheetIterator(
$filePath,
$this->optionsManager,
$this->sharedStringsManager
);
} else {
throw new IOException("Could not open {$filePath} for reading.");
}
}
/**
* Returns an iterator to iterate over sheets.
*
* @return SheetIterator To iterate over sheets
*/
protected function getConcreteSheetIterator()
{
return $this->sheetIterator;
}
/**
* Closes the reader. To be used after reading the file.
*/
protected function closeReader()
{
if (null !== $this->zip) {
$this->zip->close();
}
if (null !== $this->sharedStringsManager) {
$this->sharedStringsManager->cleanup();
}
}
}

View File

@ -1,82 +0,0 @@
<?php
namespace OpenSpout\Reader\XLSX;
use OpenSpout\Reader\SheetInterface;
/**
* Represents a sheet within a XLSX file.
*/
class Sheet implements SheetInterface
{
/** @var \OpenSpout\Reader\XLSX\RowIterator To iterate over sheet's rows */
protected $rowIterator;
/** @var int Index of the sheet, based on order in the workbook (zero-based) */
protected $index;
/** @var string Name of the sheet */
protected $name;
/** @var bool Whether the sheet was the active one */
protected $isActive;
/** @var bool Whether the sheet is visible */
protected $isVisible;
/**
* @param RowIterator $rowIterator The corresponding row iterator
* @param int $sheetIndex Index of the sheet, based on order in the workbook (zero-based)
* @param string $sheetName Name of the sheet
* @param bool $isSheetActive Whether the sheet was defined as active
* @param bool $isSheetVisible Whether the sheet is visible
*/
public function __construct($rowIterator, $sheetIndex, $sheetName, $isSheetActive, $isSheetVisible)
{
$this->rowIterator = $rowIterator;
$this->index = $sheetIndex;
$this->name = $sheetName;
$this->isActive = $isSheetActive;
$this->isVisible = $isSheetVisible;
}
/**
* @return \OpenSpout\Reader\XLSX\RowIterator
*/
public function getRowIterator()
{
return $this->rowIterator;
}
/**
* @return int Index of the sheet, based on order in the workbook (zero-based)
*/
public function getIndex()
{
return $this->index;
}
/**
* @return string Name of the sheet
*/
public function getName()
{
return $this->name;
}
/**
* @return bool Whether the sheet was defined as active
*/
public function isActive()
{
return $this->isActive;
}
/**
* @return bool Whether the sheet is visible
*/
public function isVisible()
{
return $this->isVisible;
}
}

View File

@ -1,113 +0,0 @@
<?php
namespace OpenSpout\Reader\XLSX;
use OpenSpout\Reader\Exception\NoSheetsFoundException;
use OpenSpout\Reader\IteratorInterface;
use OpenSpout\Reader\XLSX\Manager\SheetManager;
/**
* Iterate over XLSX sheet.
*/
class SheetIterator implements IteratorInterface
{
/** @var \OpenSpout\Reader\XLSX\Sheet[] The list of sheet present in the file */
protected $sheets;
/** @var int The index of the sheet being read (zero-based) */
protected $currentSheetIndex;
/**
* @param SheetManager $sheetManager Manages sheets
*
* @throws \OpenSpout\Reader\Exception\NoSheetsFoundException If there are no sheets in the file
*/
public function __construct($sheetManager)
{
// Fetch all available sheets
$this->sheets = $sheetManager->getSheets();
if (0 === \count($this->sheets)) {
throw new NoSheetsFoundException('The file must contain at least one sheet.');
}
}
/**
* Rewind the Iterator to the first element.
*
* @see http://php.net/manual/en/iterator.rewind.php
*/
#[\ReturnTypeWillChange]
public function rewind()
{
$this->currentSheetIndex = 0;
}
/**
* Checks if current position is valid.
*
* @see http://php.net/manual/en/iterator.valid.php
*
* @return bool
*/
#[\ReturnTypeWillChange]
public function valid()
{
return $this->currentSheetIndex < \count($this->sheets);
}
/**
* Move forward to next element.
*
* @see http://php.net/manual/en/iterator.next.php
*/
#[\ReturnTypeWillChange]
public function next()
{
// Using isset here because it is way faster than array_key_exists...
if (isset($this->sheets[$this->currentSheetIndex])) {
$currentSheet = $this->sheets[$this->currentSheetIndex];
$currentSheet->getRowIterator()->end();
++$this->currentSheetIndex;
}
}
/**
* Return the current element.
*
* @see http://php.net/manual/en/iterator.current.php
*
* @return \OpenSpout\Reader\XLSX\Sheet
*/
#[\ReturnTypeWillChange]
public function current()
{
return $this->sheets[$this->currentSheetIndex];
}
/**
* Return the key of the current element.
*
* @see http://php.net/manual/en/iterator.key.php
*
* @return int
*/
#[\ReturnTypeWillChange]
public function key()
{
return $this->currentSheetIndex + 1;
}
/**
* Cleans up what was created to iterate over the object.
*/
#[\ReturnTypeWillChange]
public function end()
{
// make sure we are not leaking memory in case the iteration stopped before the end
foreach ($this->sheets as $sheet) {
$sheet->getRowIterator()->end();
}
}
}

View File

@ -1,34 +0,0 @@
<?php
namespace OpenSpout\Writer\CSV\Manager;
use OpenSpout\Common\Manager\OptionsManagerAbstract;
use OpenSpout\Writer\Common\Entity\Options;
/**
* CSV Writer options manager.
*/
class OptionsManager extends OptionsManagerAbstract
{
/**
* {@inheritdoc}
*/
protected function getSupportedOptions()
{
return [
Options::FIELD_DELIMITER,
Options::FIELD_ENCLOSURE,
Options::SHOULD_ADD_BOM,
];
}
/**
* {@inheritdoc}
*/
protected function setDefaultOptions()
{
$this->setOption(Options::FIELD_DELIMITER, ',');
$this->setOption(Options::FIELD_ENCLOSURE, '"');
$this->setOption(Options::SHOULD_ADD_BOM, true);
}
}

View File

@ -1,109 +0,0 @@
<?php
namespace OpenSpout\Writer\CSV;
use OpenSpout\Common\Entity\Row;
use OpenSpout\Common\Exception\IOException;
use OpenSpout\Common\Helper\EncodingHelper;
use OpenSpout\Writer\Common\Entity\Options;
use OpenSpout\Writer\WriterAbstract;
/**
* This class provides support to write data to CSV files.
*/
class Writer extends WriterAbstract
{
/** Number of rows to write before flushing */
public const FLUSH_THRESHOLD = 500;
/** @var string Content-Type value for the header */
protected static $headerContentType = 'text/csv; charset=UTF-8';
/** @var int */
protected $lastWrittenRowIndex = 0;
/**
* Sets the field delimiter for the CSV.
*
* @param string $fieldDelimiter Character that delimits fields
*
* @return Writer
*/
public function setFieldDelimiter($fieldDelimiter)
{
$this->optionsManager->setOption(Options::FIELD_DELIMITER, $fieldDelimiter);
return $this;
}
/**
* Sets the field enclosure for the CSV.
*
* @param string $fieldEnclosure Character that enclose fields
*
* @return Writer
*/
public function setFieldEnclosure($fieldEnclosure)
{
$this->optionsManager->setOption(Options::FIELD_ENCLOSURE, $fieldEnclosure);
return $this;
}
/**
* Set if a BOM has to be added to the file.
*
* @param bool $shouldAddBOM
*
* @return Writer
*/
public function setShouldAddBOM($shouldAddBOM)
{
$this->optionsManager->setOption(Options::SHOULD_ADD_BOM, (bool) $shouldAddBOM);
return $this;
}
/**
* Opens the CSV streamer and makes it ready to accept data.
*/
protected function openWriter()
{
if ($this->optionsManager->getOption(Options::SHOULD_ADD_BOM)) {
// Adds UTF-8 BOM for Unicode compatibility
$this->globalFunctionsHelper->fputs($this->filePointer, EncodingHelper::BOM_UTF8);
}
}
/**
* Adds a row to the currently opened writer.
*
* @param Row $row The row containing cells and styles
*
* @throws IOException If unable to write data
*/
protected function addRowToWriter(Row $row)
{
$fieldDelimiter = $this->optionsManager->getOption(Options::FIELD_DELIMITER);
$fieldEnclosure = $this->optionsManager->getOption(Options::FIELD_ENCLOSURE);
$wasWriteSuccessful = $this->globalFunctionsHelper->fputcsv($this->filePointer, $row->getCells(), $fieldDelimiter, $fieldEnclosure);
if (false === $wasWriteSuccessful) {
throw new IOException('Unable to write data');
}
++$this->lastWrittenRowIndex;
if (0 === $this->lastWrittenRowIndex % self::FLUSH_THRESHOLD) {
$this->globalFunctionsHelper->fflush($this->filePointer);
}
}
/**
* Closes the CSV streamer, preventing any additional writing.
* If set, sets the headers and redirects output to the browser.
*/
protected function closeWriter()
{
$this->lastWrittenRowIndex = 0;
}
}

View File

@ -1,52 +0,0 @@
<?php
namespace OpenSpout\Writer\Common\Creator;
use OpenSpout\Writer\Common\Entity\Sheet;
use OpenSpout\Writer\Common\Entity\Workbook;
use OpenSpout\Writer\Common\Entity\Worksheet;
use OpenSpout\Writer\Common\Manager\SheetManager;
/**
* Factory to create internal entities.
*/
class InternalEntityFactory
{
/**
* @return Workbook
*/
public function createWorkbook()
{
return new Workbook();
}
/**
* @param string $worksheetFilePath
*
* @return Worksheet
*/
public function createWorksheet($worksheetFilePath, Sheet $externalSheet)
{
return new Worksheet($worksheetFilePath, $externalSheet);
}
/**
* @param int $sheetIndex Index of the sheet, based on order in the workbook (zero-based)
* @param string $associatedWorkbookId ID of the sheet's associated workbook
* @param SheetManager $sheetManager To manage sheets
*
* @return Sheet
*/
public function createSheet($sheetIndex, $associatedWorkbookId, $sheetManager)
{
return new Sheet($sheetIndex, $associatedWorkbookId, $sheetManager);
}
/**
* @return \ZipArchive
*/
public function createZipArchive()
{
return new \ZipArchive();
}
}

View File

@ -1,23 +0,0 @@
<?php
namespace OpenSpout\Writer\Common\Creator;
use OpenSpout\Common\Manager\OptionsManagerInterface;
use OpenSpout\Writer\Common\Manager\SheetManager;
use OpenSpout\Writer\Common\Manager\WorkbookManagerInterface;
/**
* Interface ManagerFactoryInterface.
*/
interface ManagerFactoryInterface
{
/**
* @return WorkbookManagerInterface
*/
public function createWorkbookManager(OptionsManagerInterface $optionsManager);
/**
* @return SheetManager
*/
public function createSheetManager();
}

View File

@ -1,84 +0,0 @@
<?php
namespace OpenSpout\Writer\Common\Creator\Style;
use OpenSpout\Common\Entity\Style\Border;
use OpenSpout\Common\Entity\Style\BorderPart;
use OpenSpout\Common\Entity\Style\Color;
class BorderBuilder
{
/**
* @var Border
*/
protected $border;
public function __construct()
{
$this->border = new Border();
}
/**
* @param string $color Border A RGB color code
* @param string $width Border width @see BorderPart::allowedWidths
* @param string $style Border style @see BorderPart::allowedStyles
*
* @return BorderBuilder
*/
public function setBorderTop($color = Color::BLACK, $width = Border::WIDTH_MEDIUM, $style = Border::STYLE_SOLID)
{
$this->border->addPart(new BorderPart(Border::TOP, $color, $width, $style));
return $this;
}
/**
* @param string $color Border A RGB color code
* @param string $width Border width @see BorderPart::allowedWidths
* @param string $style Border style @see BorderPart::allowedStyles
*
* @return BorderBuilder
*/
public function setBorderRight($color = Color::BLACK, $width = Border::WIDTH_MEDIUM, $style = Border::STYLE_SOLID)
{
$this->border->addPart(new BorderPart(Border::RIGHT, $color, $width, $style));
return $this;
}
/**
* @param string $color Border A RGB color code
* @param string $width Border width @see BorderPart::allowedWidths
* @param string $style Border style @see BorderPart::allowedStyles
*
* @return BorderBuilder
*/
public function setBorderBottom($color = Color::BLACK, $width = Border::WIDTH_MEDIUM, $style = Border::STYLE_SOLID)
{
$this->border->addPart(new BorderPart(Border::BOTTOM, $color, $width, $style));
return $this;
}
/**
* @param string $color Border A RGB color code
* @param string $width Border width @see BorderPart::allowedWidths
* @param string $style Border style @see BorderPart::allowedStyles
*
* @return BorderBuilder
*/
public function setBorderLeft($color = Color::BLACK, $width = Border::WIDTH_MEDIUM, $style = Border::STYLE_SOLID)
{
$this->border->addPart(new BorderPart(Border::LEFT, $color, $width, $style));
return $this;
}
/**
* @return Border
*/
public function build()
{
return $this->border;
}
}

View File

@ -1,214 +0,0 @@
<?php
namespace OpenSpout\Writer\Common\Creator\Style;
use OpenSpout\Common\Entity\Style\Border;
use OpenSpout\Common\Entity\Style\CellAlignment;
use OpenSpout\Common\Entity\Style\Style;
use OpenSpout\Common\Exception\InvalidArgumentException;
/**
* Builder to create new styles.
*/
class StyleBuilder
{
/** @var Style Style to be created */
protected $style;
public function __construct()
{
$this->style = new Style();
}
/**
* Makes the font bold.
*
* @return StyleBuilder
*/
public function setFontBold()
{
$this->style->setFontBold();
return $this;
}
/**
* Makes the font italic.
*
* @return StyleBuilder
*/
public function setFontItalic()
{
$this->style->setFontItalic();
return $this;
}
/**
* Makes the font underlined.
*
* @return StyleBuilder
*/
public function setFontUnderline()
{
$this->style->setFontUnderline();
return $this;
}
/**
* Makes the font struck through.
*
* @return StyleBuilder
*/
public function setFontStrikethrough()
{
$this->style->setFontStrikethrough();
return $this;
}
/**
* Sets the font size.
*
* @param int $fontSize Font size, in pixels
*
* @return StyleBuilder
*/
public function setFontSize($fontSize)
{
$this->style->setFontSize($fontSize);
return $this;
}
/**
* Sets the font color.
*
* @param string $fontColor ARGB color (@see Color)
*
* @return StyleBuilder
*/
public function setFontColor($fontColor)
{
$this->style->setFontColor($fontColor);
return $this;
}
/**
* Sets the font name.
*
* @param string $fontName Name of the font to use
*
* @return StyleBuilder
*/
public function setFontName($fontName)
{
$this->style->setFontName($fontName);
return $this;
}
/**
* Makes the text wrap in the cell if requested.
*
* @param bool $shouldWrap Should the text be wrapped
*
* @return StyleBuilder
*/
public function setShouldWrapText($shouldWrap = true)
{
$this->style->setShouldWrapText($shouldWrap);
return $this;
}
/**
* Sets the cell alignment.
*
* @param string $cellAlignment The cell alignment
*
* @throws InvalidArgumentException If the given cell alignment is not valid
*
* @return StyleBuilder
*/
public function setCellAlignment($cellAlignment)
{
if (!CellAlignment::isValid($cellAlignment)) {
throw new InvalidArgumentException('Invalid cell alignment value');
}
$this->style->setCellAlignment($cellAlignment);
return $this;
}
/**
* Set a border.
*
* @return $this
*/
public function setBorder(Border $border)
{
$this->style->setBorder($border);
return $this;
}
/**
* Sets a background color.
*
* @param string $color ARGB color (@see Color)
*
* @return StyleBuilder
*/
public function setBackgroundColor($color)
{
$this->style->setBackgroundColor($color);
return $this;
}
/**
* Sets a format.
*
* @param string $format Format
*
* @return StyleBuilder
*
* @api
*/
public function setFormat($format)
{
$this->style->setFormat($format);
return $this;
}
/**
* Set should shrink to fit.
*
* @param bool $shrinkToFit
*
* @return StyleBuilder
*
* @api
*/
public function setShouldShrinkToFit($shrinkToFit = true)
{
$this->style->setShouldShrinkToFit($shrinkToFit);
return $this;
}
/**
* Returns the configured style. The style is cached and can be reused.
*
* @return Style
*/
public function build()
{
return $this->style;
}
}

View File

@ -1,121 +0,0 @@
<?php
namespace OpenSpout\Writer\Common\Creator;
use OpenSpout\Common\Entity\Cell;
use OpenSpout\Common\Entity\Row;
use OpenSpout\Common\Entity\Style\Style;
use OpenSpout\Common\Exception\UnsupportedTypeException;
use OpenSpout\Common\Type;
use OpenSpout\Writer\WriterInterface;
/**
* Factory to create external entities.
*/
class WriterEntityFactory
{
/**
* This creates an instance of the appropriate writer, given the type of the file to be written.
*
* @param string $writerType Type of the writer to instantiate
*
* @throws \OpenSpout\Common\Exception\UnsupportedTypeException
*
* @return WriterInterface
*/
public static function createWriter($writerType)
{
return WriterFactory::createFromType($writerType);
}
/**
* This creates an instance of the appropriate writer, given the extension of the file to be written.
*
* @param string $path The path to the spreadsheet file. Supported extensions are .csv, .ods and .xlsx
*
* @throws \OpenSpout\Common\Exception\UnsupportedTypeException
*
* @return WriterInterface
*/
public static function createWriterFromFile(string $path)
{
return WriterFactory::createFromFile($path);
}
/**
* This creates an instance of a CSV writer.
*
* @return \OpenSpout\Writer\CSV\Writer
*/
public static function createCSVWriter()
{
try {
return WriterFactory::createFromType(Type::CSV);
} catch (UnsupportedTypeException $e) {
// should never happen
return null;
}
}
/**
* This creates an instance of a XLSX writer.
*
* @return \OpenSpout\Writer\XLSX\Writer
*/
public static function createXLSXWriter()
{
try {
return WriterFactory::createFromType(Type::XLSX);
} catch (UnsupportedTypeException $e) {
// should never happen
return null;
}
}
/**
* This creates an instance of a ODS writer.
*
* @return \OpenSpout\Writer\ODS\Writer
*/
public static function createODSWriter()
{
try {
return WriterFactory::createFromType(Type::ODS);
} catch (UnsupportedTypeException $e) {
// should never happen
return null;
}
}
/**
* @param Cell[] $cells
*
* @return Row
*/
public static function createRow(array $cells = [], Style $rowStyle = null)
{
return new Row($cells, $rowStyle);
}
/**
* @return Row
*/
public static function createRowFromArray(array $cellValues = [], Style $rowStyle = null)
{
$cells = array_map(function ($cellValue) {
return new Cell($cellValue);
}, $cellValues);
return new Row($cells, $rowStyle);
}
/**
* @param mixed $cellValue
*
* @return Cell
*/
public static function createCell($cellValue, Style $cellStyle = null)
{
return new Cell($cellValue, $cellStyle);
}
}

View File

@ -1,109 +0,0 @@
<?php
namespace OpenSpout\Writer\Common\Creator;
use OpenSpout\Common\Creator\HelperFactory;
use OpenSpout\Common\Exception\UnsupportedTypeException;
use OpenSpout\Common\Helper\GlobalFunctionsHelper;
use OpenSpout\Common\Type;
use OpenSpout\Writer\Common\Creator\Style\StyleBuilder;
use OpenSpout\Writer\CSV\Manager\OptionsManager as CSVOptionsManager;
use OpenSpout\Writer\CSV\Writer as CSVWriter;
use OpenSpout\Writer\ODS\Creator\HelperFactory as ODSHelperFactory;
use OpenSpout\Writer\ODS\Creator\ManagerFactory as ODSManagerFactory;
use OpenSpout\Writer\ODS\Manager\OptionsManager as ODSOptionsManager;
use OpenSpout\Writer\ODS\Writer as ODSWriter;
use OpenSpout\Writer\WriterInterface;
use OpenSpout\Writer\XLSX\Creator\HelperFactory as XLSXHelperFactory;
use OpenSpout\Writer\XLSX\Creator\ManagerFactory as XLSXManagerFactory;
use OpenSpout\Writer\XLSX\Manager\OptionsManager as XLSXOptionsManager;
use OpenSpout\Writer\XLSX\Writer as XLSXWriter;
/**
* This factory is used to create writers, based on the type of the file to be read.
* It supports CSV, XLSX and ODS formats.
*/
class WriterFactory
{
/**
* This creates an instance of the appropriate writer, given the extension of the file to be written.
*
* @param string $path The path to the spreadsheet file. Supported extensions are .csv,.ods and .xlsx
*
* @throws \OpenSpout\Common\Exception\UnsupportedTypeException
*
* @return WriterInterface
*/
public static function createFromFile(string $path)
{
$extension = strtolower(pathinfo($path, PATHINFO_EXTENSION));
return self::createFromType($extension);
}
/**
* This creates an instance of the appropriate writer, given the type of the file to be written.
*
* @param string $writerType Type of the writer to instantiate
*
* @throws \OpenSpout\Common\Exception\UnsupportedTypeException
*
* @return WriterInterface
*/
public static function createFromType($writerType)
{
switch ($writerType) {
case Type::CSV: return self::createCSVWriter();
case Type::XLSX: return self::createXLSXWriter();
case Type::ODS: return self::createODSWriter();
default:
throw new UnsupportedTypeException('No writers supporting the given type: '.$writerType);
}
}
/**
* @return CSVWriter
*/
private static function createCSVWriter()
{
$optionsManager = new CSVOptionsManager();
$globalFunctionsHelper = new GlobalFunctionsHelper();
$helperFactory = new HelperFactory();
return new CSVWriter($optionsManager, $globalFunctionsHelper, $helperFactory);
}
/**
* @return XLSXWriter
*/
private static function createXLSXWriter()
{
$styleBuilder = new StyleBuilder();
$optionsManager = new XLSXOptionsManager($styleBuilder);
$globalFunctionsHelper = new GlobalFunctionsHelper();
$helperFactory = new XLSXHelperFactory();
$managerFactory = new XLSXManagerFactory(new InternalEntityFactory(), $helperFactory);
return new XLSXWriter($optionsManager, $globalFunctionsHelper, $helperFactory, $managerFactory);
}
/**
* @return ODSWriter
*/
private static function createODSWriter()
{
$styleBuilder = new StyleBuilder();
$optionsManager = new ODSOptionsManager($styleBuilder);
$globalFunctionsHelper = new GlobalFunctionsHelper();
$helperFactory = new ODSHelperFactory();
$managerFactory = new ODSManagerFactory(new InternalEntityFactory(), $helperFactory);
return new ODSWriter($optionsManager, $globalFunctionsHelper, $helperFactory, $managerFactory);
}
}

View File

@ -1,30 +0,0 @@
<?php
namespace OpenSpout\Writer\Common\Entity;
/**
* Writers' options holder.
*/
abstract class Options
{
// CSV specific options
public const FIELD_DELIMITER = 'fieldDelimiter';
public const FIELD_ENCLOSURE = 'fieldEnclosure';
public const SHOULD_ADD_BOM = 'shouldAddBOM';
// Multisheets options
public const TEMP_FOLDER = 'tempFolder';
public const DEFAULT_ROW_STYLE = 'defaultRowStyle';
public const SHOULD_CREATE_NEW_SHEETS_AUTOMATICALLY = 'shouldCreateNewSheetsAutomatically';
// XLSX specific options
public const SHOULD_USE_INLINE_STRINGS = 'shouldUseInlineStrings';
// Cell size options
public const DEFAULT_COLUMN_WIDTH = 'defaultColumnWidth';
public const DEFAULT_ROW_HEIGHT = 'defaultRowHeight';
public const COLUMN_WIDTHS = 'columnWidthDefinition';
// XLSX merge cells
public const MERGE_CELLS = 'mergeCells';
}

View File

@ -1,137 +0,0 @@
<?php
namespace OpenSpout\Writer\Common\Entity;
use OpenSpout\Writer\Common\Manager\SheetManager;
use OpenSpout\Writer\XLSX\Entity\SheetView;
/**
* External representation of a worksheet.
*/
class Sheet
{
public const DEFAULT_SHEET_NAME_PREFIX = 'Sheet';
/** @var int Index of the sheet, based on order in the workbook (zero-based) */
private $index;
/** @var string ID of the sheet's associated workbook. Used to restrict sheet name uniqueness enforcement to a single workbook */
private $associatedWorkbookId;
/** @var string Name of the sheet */
private $name;
/** @var bool Visibility of the sheet */
private $isVisible;
/** @var SheetManager Sheet manager */
private $sheetManager;
/** @var SheetView */
private $sheetView;
/**
* @param int $sheetIndex Index of the sheet, based on order in the workbook (zero-based)
* @param string $associatedWorkbookId ID of the sheet's associated workbook
* @param SheetManager $sheetManager To manage sheets
*/
public function __construct($sheetIndex, $associatedWorkbookId, SheetManager $sheetManager)
{
$this->index = $sheetIndex;
$this->associatedWorkbookId = $associatedWorkbookId;
$this->sheetManager = $sheetManager;
$this->sheetManager->markWorkbookIdAsUsed($associatedWorkbookId);
$this->setName(self::DEFAULT_SHEET_NAME_PREFIX.($sheetIndex + 1));
$this->setIsVisible(true);
}
/**
* @return int Index of the sheet, based on order in the workbook (zero-based)
*/
public function getIndex()
{
return $this->index;
}
/**
* @return string
*/
public function getAssociatedWorkbookId()
{
return $this->associatedWorkbookId;
}
/**
* @return string Name of the sheet
*/
public function getName()
{
return $this->name;
}
/**
* Sets the name of the sheet. Note that Excel has some restrictions on the name:
* - it should not be blank
* - it should not exceed 31 characters
* - it should not contain these characters: \ / ? * : [ or ]
* - it should be unique.
*
* @param string $name Name of the sheet
*
* @throws \OpenSpout\Writer\Exception\InvalidSheetNameException if the sheet's name is invalid
*
* @return Sheet
*/
public function setName($name)
{
$this->sheetManager->throwIfNameIsInvalid($name, $this);
$this->name = $name;
$this->sheetManager->markSheetNameAsUsed($this);
return $this;
}
/**
* @return bool isVisible Visibility of the sheet
*/
public function isVisible()
{
return $this->isVisible;
}
/**
* @param bool $isVisible Visibility of the sheet
*
* @return Sheet
*/
public function setIsVisible($isVisible)
{
$this->isVisible = $isVisible;
return $this;
}
public function getSheetView(): ?SheetView
{
return $this->sheetView;
}
/**
* @return $this
*/
public function setSheetView(SheetView $sheetView)
{
$this->sheetView = $sheetView;
return $this;
}
public function hasSheetView(): bool
{
return $this->sheetView instanceof SheetView;
}
}

View File

@ -1,131 +0,0 @@
<?php
namespace OpenSpout\Writer\Common\Entity;
/**
* Entity describing a Worksheet.
*/
class Worksheet
{
/** @var string Path to the XML file that will contain the sheet data */
private $filePath;
/** @var null|resource Pointer to the sheet data file (e.g. xl/worksheets/sheet1.xml) */
private $filePointer;
/** @var Sheet The "external" sheet */
private $externalSheet;
/** @var int Maximum number of columns among all the written rows */
private $maxNumColumns;
/** @var int Index of the last written row */
private $lastWrittenRowIndex;
/** @var bool has the sheet data header been written */
private $sheetDataStarted = false;
/**
* Worksheet constructor.
*
* @param string $worksheetFilePath
*/
public function __construct($worksheetFilePath, Sheet $externalSheet)
{
$this->filePath = $worksheetFilePath;
$this->filePointer = null;
$this->externalSheet = $externalSheet;
$this->maxNumColumns = 0;
$this->lastWrittenRowIndex = 0;
$this->sheetDataStarted = false;
}
/**
* @return string
*/
public function getFilePath()
{
return $this->filePath;
}
/**
* @return resource
*/
public function getFilePointer()
{
return $this->filePointer;
}
/**
* @param resource $filePointer
*/
public function setFilePointer($filePointer)
{
$this->filePointer = $filePointer;
}
/**
* @return Sheet
*/
public function getExternalSheet()
{
return $this->externalSheet;
}
/**
* @return int
*/
public function getMaxNumColumns()
{
return $this->maxNumColumns;
}
/**
* @param int $maxNumColumns
*/
public function setMaxNumColumns($maxNumColumns)
{
$this->maxNumColumns = $maxNumColumns;
}
/**
* @return int
*/
public function getLastWrittenRowIndex()
{
return $this->lastWrittenRowIndex;
}
/**
* @param int $lastWrittenRowIndex
*/
public function setLastWrittenRowIndex($lastWrittenRowIndex)
{
$this->lastWrittenRowIndex = $lastWrittenRowIndex;
}
/**
* @return int The ID of the worksheet
*/
public function getId()
{
// sheet index is zero-based, while ID is 1-based
return $this->externalSheet->getIndex() + 1;
}
/**
* @return bool
*/
public function getSheetDataStarted()
{
return $this->sheetDataStarted;
}
/**
* @param bool $sheetDataStarted
*/
public function setSheetDataStarted($sheetDataStarted)
{
$this->sheetDataStarted = $sheetDataStarted;
}
}

View File

@ -1,24 +0,0 @@
<?php
namespace OpenSpout\Writer\Common\Helper;
use OpenSpout\Common\Helper\FileSystemHelperInterface;
/**
* This interface describes helper functions to help with the file system operations
* like files/folders creation & deletion.
*/
interface FileSystemWithRootFolderHelperInterface extends FileSystemHelperInterface
{
/**
* Creates all the folders needed to create a spreadsheet, as well as the files that won't change.
*
* @throws \OpenSpout\Common\Exception\IOException If unable to create at least one of the base folders
*/
public function createBaseFilesAndFolders();
/**
* @return string
*/
public function getRootFolder();
}

View File

@ -1,214 +0,0 @@
<?php
namespace OpenSpout\Writer\Common\Helper;
use OpenSpout\Writer\Common\Creator\InternalEntityFactory;
/**
* This class provides helper functions to create zip files.
*/
class ZipHelper
{
public const ZIP_EXTENSION = '.zip';
/** Controls what to do when trying to add an existing file */
public const EXISTING_FILES_SKIP = 'skip';
public const EXISTING_FILES_OVERWRITE = 'overwrite';
/** @var InternalEntityFactory Factory to create entities */
private $entityFactory;
/**
* @param InternalEntityFactory $entityFactory Factory to create entities
*/
public function __construct($entityFactory)
{
$this->entityFactory = $entityFactory;
}
/**
* Returns a new ZipArchive instance pointing at the given path.
*
* @param string $tmpFolderPath Path of the temp folder where the zip file will be created
*
* @return \ZipArchive
*/
public function createZip($tmpFolderPath)
{
$zip = $this->entityFactory->createZipArchive();
$zipFilePath = $tmpFolderPath.self::ZIP_EXTENSION;
$zip->open($zipFilePath, \ZipArchive::CREATE | \ZipArchive::OVERWRITE);
return $zip;
}
/**
* @param \ZipArchive $zip An opened zip archive object
*
* @return string Path where the zip file of the given folder will be created
*/
public function getZipFilePath(\ZipArchive $zip)
{
return $zip->filename;
}
/**
* Adds the given file, located under the given root folder to the archive.
* The file will be compressed.
*
* Example of use:
* addFileToArchive($zip, '/tmp/xlsx/foo', 'bar/baz.xml');
* => will add the file located at '/tmp/xlsx/foo/bar/baz.xml' in the archive, but only as 'bar/baz.xml'
*
* @param \ZipArchive $zip An opened zip archive object
* @param string $rootFolderPath path of the root folder that will be ignored in the archive tree
* @param string $localFilePath Path of the file to be added, under the root folder
* @param string $existingFileMode Controls what to do when trying to add an existing file
*/
public function addFileToArchive($zip, $rootFolderPath, $localFilePath, $existingFileMode = self::EXISTING_FILES_OVERWRITE)
{
$this->addFileToArchiveWithCompressionMethod(
$zip,
$rootFolderPath,
$localFilePath,
$existingFileMode,
\ZipArchive::CM_DEFAULT
);
}
/**
* Adds the given file, located under the given root folder to the archive.
* The file will NOT be compressed.
*
* Example of use:
* addUncompressedFileToArchive($zip, '/tmp/xlsx/foo', 'bar/baz.xml');
* => will add the file located at '/tmp/xlsx/foo/bar/baz.xml' in the archive, but only as 'bar/baz.xml'
*
* @param \ZipArchive $zip An opened zip archive object
* @param string $rootFolderPath path of the root folder that will be ignored in the archive tree
* @param string $localFilePath Path of the file to be added, under the root folder
* @param string $existingFileMode Controls what to do when trying to add an existing file
*/
public function addUncompressedFileToArchive($zip, $rootFolderPath, $localFilePath, $existingFileMode = self::EXISTING_FILES_OVERWRITE)
{
$this->addFileToArchiveWithCompressionMethod(
$zip,
$rootFolderPath,
$localFilePath,
$existingFileMode,
\ZipArchive::CM_STORE
);
}
/**
* @return bool Whether it is possible to choose the desired compression method to be used
*/
public static function canChooseCompressionMethod()
{
// setCompressionName() is a PHP7+ method...
return method_exists(new \ZipArchive(), 'setCompressionName');
}
/**
* @param \ZipArchive $zip An opened zip archive object
* @param string $folderPath Path to the folder to be zipped
* @param string $existingFileMode Controls what to do when trying to add an existing file
*/
public function addFolderToArchive($zip, $folderPath, $existingFileMode = self::EXISTING_FILES_OVERWRITE)
{
$folderRealPath = $this->getNormalizedRealPath($folderPath).'/';
$itemIterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($folderPath, \RecursiveDirectoryIterator::SKIP_DOTS), \RecursiveIteratorIterator::SELF_FIRST);
foreach ($itemIterator as $itemInfo) {
$itemRealPath = $this->getNormalizedRealPath($itemInfo->getPathname());
$itemLocalPath = str_replace($folderRealPath, '', $itemRealPath);
if ($itemInfo->isFile() && !$this->shouldSkipFile($zip, $itemLocalPath, $existingFileMode)) {
$zip->addFile($itemRealPath, $itemLocalPath);
}
}
}
/**
* Closes the archive and copies it into the given stream.
*
* @param \ZipArchive $zip An opened zip archive object
* @param resource $streamPointer Pointer to the stream to copy the zip
*/
public function closeArchiveAndCopyToStream($zip, $streamPointer)
{
$zipFilePath = $zip->filename;
$zip->close();
$this->copyZipToStream($zipFilePath, $streamPointer);
}
/**
* Adds the given file, located under the given root folder to the archive.
* The file will NOT be compressed.
*
* Example of use:
* addUncompressedFileToArchive($zip, '/tmp/xlsx/foo', 'bar/baz.xml');
* => will add the file located at '/tmp/xlsx/foo/bar/baz.xml' in the archive, but only as 'bar/baz.xml'
*
* @param \ZipArchive $zip An opened zip archive object
* @param string $rootFolderPath path of the root folder that will be ignored in the archive tree
* @param string $localFilePath Path of the file to be added, under the root folder
* @param string $existingFileMode Controls what to do when trying to add an existing file
* @param int $compressionMethod The compression method
*/
protected function addFileToArchiveWithCompressionMethod($zip, $rootFolderPath, $localFilePath, $existingFileMode, $compressionMethod)
{
if (!$this->shouldSkipFile($zip, $localFilePath, $existingFileMode)) {
$normalizedFullFilePath = $this->getNormalizedRealPath($rootFolderPath.'/'.$localFilePath);
$zip->addFile($normalizedFullFilePath, $localFilePath);
if (self::canChooseCompressionMethod()) {
$zip->setCompressionName($localFilePath, $compressionMethod);
}
}
}
/**
* @param \ZipArchive $zip
* @param string $itemLocalPath
* @param string $existingFileMode
*
* @return bool Whether the file should be added to the archive or skipped
*/
protected function shouldSkipFile($zip, $itemLocalPath, $existingFileMode)
{
// Skip files if:
// - EXISTING_FILES_SKIP mode chosen
// - File already exists in the archive
return self::EXISTING_FILES_SKIP === $existingFileMode && false !== $zip->locateName($itemLocalPath);
}
/**
* Returns canonicalized absolute pathname, containing only forward slashes.
*
* @param string $path Path to normalize
*
* @return string Normalized and canonicalized path
*/
protected function getNormalizedRealPath($path)
{
$realPath = realpath($path);
return str_replace(\DIRECTORY_SEPARATOR, '/', $realPath);
}
/**
* Streams the contents of the zip file into the given stream.
*
* @param string $zipFilePath Path of the zip file
* @param resource $pointer Pointer to the stream to copy the zip
*/
protected function copyZipToStream($zipFilePath, $pointer)
{
$zipFilePointer = fopen($zipFilePath, 'r');
stream_copy_to_stream($zipFilePointer, $pointer);
fclose($zipFilePointer);
}
}

View File

@ -1,29 +0,0 @@
<?php
namespace OpenSpout\Writer\Common\Manager;
use OpenSpout\Common\Entity\Cell;
use OpenSpout\Common\Entity\Style\Style;
use OpenSpout\Writer\Common\Manager\Style\StyleMerger;
class CellManager
{
/**
* @var StyleMerger
*/
protected $styleMerger;
public function __construct(StyleMerger $styleMerger)
{
$this->styleMerger = $styleMerger;
}
/**
* Merges a Style into a cell's Style.
*/
public function applyStyle(Cell $cell, Style $style)
{
$mergedStyle = $this->styleMerger->merge($cell->getStyle(), $style);
$cell->setStyle($mergedStyle);
}
}

View File

@ -1,62 +0,0 @@
<?php
namespace OpenSpout\Writer\Common\Manager;
trait ManagesCellSize
{
/** @var null|float The default column width to use */
private $defaultColumnWidth;
/** @var null|float The default row height to use */
private $defaultRowHeight;
/** @var array Array of min-max-width arrays */
private $columnWidths = [];
/**
* @param null|float $width
*/
public function setDefaultColumnWidth($width)
{
$this->defaultColumnWidth = $width;
}
/**
* @param null|float $height
*/
public function setDefaultRowHeight($height)
{
$this->defaultRowHeight = $height;
}
/**
* @param int ...$columns One or more columns with this width
*/
public function setColumnWidth(float $width, ...$columns)
{
// Gather sequences
$sequence = [];
foreach ($columns as $i) {
$sequenceLength = \count($sequence);
if ($sequenceLength > 0) {
$previousValue = $sequence[$sequenceLength - 1];
if ($i !== $previousValue + 1) {
$this->setColumnWidthForRange($width, $sequence[0], $previousValue);
$sequence = [];
}
}
$sequence[] = $i;
}
$this->setColumnWidthForRange($width, $sequence[0], $sequence[\count($sequence) - 1]);
}
/**
* @param float $width The width to set
* @param int $start First column index of the range
* @param int $end Last column index of the range
*/
public function setColumnWidthForRange(float $width, int $start, int $end)
{
$this->columnWidths[] = [$start, $end, $width];
}
}

View File

@ -1,25 +0,0 @@
<?php
namespace OpenSpout\Writer\Common\Manager;
use OpenSpout\Common\Entity\Row;
class RowManager
{
/**
* Detect whether a row is considered empty.
* An empty row has all of its cells empty.
*
* @return bool
*/
public function isEmpty(Row $row)
{
foreach ($row->getCells() as $cell) {
if (!$cell->isEmpty()) {
return false;
}
}
return true;
}
}

View File

@ -1,60 +0,0 @@
<?php
namespace OpenSpout\Writer\Common\Manager;
use OpenSpout\Common\Entity\Row;
use OpenSpout\Writer\Common\Entity\Worksheet;
/**
* Interface WorksheetManagerInterface
* Inteface for worksheet managers, providing the generic interfaces to work with worksheets.
*/
interface WorksheetManagerInterface
{
/**
* @param null|float $width
*/
public function setDefaultColumnWidth($width);
/**
* @param null|float $height
*/
public function setDefaultRowHeight($height);
/**
* @param int ...$columns One or more columns with this width
*/
public function setColumnWidth(float $width, ...$columns);
/**
* @param float $width The width to set
* @param int $start First column index of the range
* @param int $end Last column index of the range
*/
public function setColumnWidthForRange(float $width, int $start, int $end);
/**
* Adds a row to the worksheet.
*
* @param Worksheet $worksheet The worksheet to add the row to
* @param Row $row The row to be added
*
* @throws \OpenSpout\Common\Exception\IOException If the data cannot be written
* @throws \OpenSpout\Common\Exception\InvalidArgumentException If a cell value's type is not supported
*/
public function addRow(Worksheet $worksheet, Row $row);
/**
* Prepares the worksheet to accept data.
*
* @param Worksheet $worksheet The worksheet to start
*
* @throws \OpenSpout\Common\Exception\IOException If the sheet data file cannot be opened for writing
*/
public function startSheet(Worksheet $worksheet);
/**
* Closes the worksheet.
*/
public function close(Worksheet $worksheet);
}

View File

@ -1,7 +0,0 @@
<?php
namespace OpenSpout\Writer\Exception;
class InvalidSheetNameException extends WriterException
{
}

View File

@ -1,7 +0,0 @@
<?php
namespace OpenSpout\Writer\Exception;
class SheetNotFoundException extends WriterException
{
}

View File

@ -1,7 +0,0 @@
<?php
namespace OpenSpout\Writer\Exception;
class WriterAlreadyOpenedException extends WriterException
{
}

View File

@ -1,9 +0,0 @@
<?php
namespace OpenSpout\Writer\Exception;
use OpenSpout\Common\Exception\SpoutException;
abstract class WriterException extends SpoutException
{
}

View File

@ -1,7 +0,0 @@
<?php
namespace OpenSpout\Writer\Exception;
class WriterNotOpenedException extends WriterException
{
}

View File

@ -1,54 +0,0 @@
<?php
namespace OpenSpout\Writer\ODS\Creator;
use OpenSpout\Common\Helper\Escaper;
use OpenSpout\Common\Helper\StringHelper;
use OpenSpout\Common\Manager\OptionsManagerInterface;
use OpenSpout\Writer\Common\Creator\InternalEntityFactory;
use OpenSpout\Writer\Common\Entity\Options;
use OpenSpout\Writer\Common\Helper\ZipHelper;
use OpenSpout\Writer\ODS\Helper\FileSystemHelper;
/**
* Factory for helpers needed by the ODS Writer.
*/
class HelperFactory extends \OpenSpout\Common\Creator\HelperFactory
{
/**
* @return FileSystemHelper
*/
public function createSpecificFileSystemHelper(OptionsManagerInterface $optionsManager, InternalEntityFactory $entityFactory)
{
$tempFolder = $optionsManager->getOption(Options::TEMP_FOLDER);
$zipHelper = $this->createZipHelper($entityFactory);
return new FileSystemHelper($tempFolder, $zipHelper);
}
/**
* @return Escaper\ODS
*/
public function createStringsEscaper()
{
return new Escaper\ODS();
}
/**
* @return StringHelper
*/
public function createStringHelper()
{
return new StringHelper();
}
/**
* @param InternalEntityFactory $entityFactory
*
* @return ZipHelper
*/
private function createZipHelper($entityFactory)
{
return new ZipHelper($entityFactory);
}
}

View File

@ -1,107 +0,0 @@
<?php
namespace OpenSpout\Writer\ODS\Creator;
use OpenSpout\Common\Manager\OptionsManagerInterface;
use OpenSpout\Writer\Common\Creator\InternalEntityFactory;
use OpenSpout\Writer\Common\Creator\ManagerFactoryInterface;
use OpenSpout\Writer\Common\Entity\Options;
use OpenSpout\Writer\Common\Manager\SheetManager;
use OpenSpout\Writer\Common\Manager\Style\StyleMerger;
use OpenSpout\Writer\ODS\Manager\Style\StyleManager;
use OpenSpout\Writer\ODS\Manager\Style\StyleRegistry;
use OpenSpout\Writer\ODS\Manager\WorkbookManager;
use OpenSpout\Writer\ODS\Manager\WorksheetManager;
/**
* Factory for managers needed by the ODS Writer.
*/
class ManagerFactory implements ManagerFactoryInterface
{
/** @var InternalEntityFactory */
protected $entityFactory;
/** @var HelperFactory */
protected $helperFactory;
public function __construct(InternalEntityFactory $entityFactory, HelperFactory $helperFactory)
{
$this->entityFactory = $entityFactory;
$this->helperFactory = $helperFactory;
}
/**
* @return WorkbookManager
*/
public function createWorkbookManager(OptionsManagerInterface $optionsManager)
{
$workbook = $this->entityFactory->createWorkbook();
$fileSystemHelper = $this->helperFactory->createSpecificFileSystemHelper($optionsManager, $this->entityFactory);
$fileSystemHelper->createBaseFilesAndFolders();
$styleMerger = $this->createStyleMerger();
$styleManager = $this->createStyleManager($optionsManager);
$worksheetManager = $this->createWorksheetManager($styleManager, $styleMerger);
return new WorkbookManager(
$workbook,
$optionsManager,
$worksheetManager,
$styleManager,
$styleMerger,
$fileSystemHelper,
$this->entityFactory,
$this
);
}
/**
* @return SheetManager
*/
public function createSheetManager()
{
$stringHelper = $this->helperFactory->createStringHelper();
return new SheetManager($stringHelper);
}
/**
* @return WorksheetManager
*/
private function createWorksheetManager(StyleManager $styleManager, StyleMerger $styleMerger)
{
$stringsEscaper = $this->helperFactory->createStringsEscaper();
$stringsHelper = $this->helperFactory->createStringHelper();
return new WorksheetManager($styleManager, $styleMerger, $stringsEscaper, $stringsHelper);
}
/**
* @return StyleManager
*/
private function createStyleManager(OptionsManagerInterface $optionsManager)
{
$styleRegistry = $this->createStyleRegistry($optionsManager);
return new StyleManager($styleRegistry, $optionsManager);
}
/**
* @return StyleRegistry
*/
private function createStyleRegistry(OptionsManagerInterface $optionsManager)
{
$defaultRowStyle = $optionsManager->getOption(Options::DEFAULT_ROW_STYLE);
return new StyleRegistry($defaultRowStyle);
}
/**
* @return StyleMerger
*/
private function createStyleMerger()
{
return new StyleMerger();
}
}

View File

@ -1,50 +0,0 @@
<?php
namespace OpenSpout\Writer\ODS\Manager;
use OpenSpout\Common\Manager\OptionsManagerAbstract;
use OpenSpout\Writer\Common\Creator\Style\StyleBuilder;
use OpenSpout\Writer\Common\Entity\Options;
/**
* ODS Writer options manager.
*/
class OptionsManager extends OptionsManagerAbstract
{
/** @var StyleBuilder Style builder */
protected $styleBuilder;
/**
* OptionsManager constructor.
*/
public function __construct(StyleBuilder $styleBuilder)
{
$this->styleBuilder = $styleBuilder;
parent::__construct();
}
/**
* {@inheritdoc}
*/
protected function getSupportedOptions()
{
return [
Options::TEMP_FOLDER,
Options::DEFAULT_ROW_STYLE,
Options::SHOULD_CREATE_NEW_SHEETS_AUTOMATICALLY,
Options::DEFAULT_COLUMN_WIDTH,
Options::DEFAULT_ROW_HEIGHT,
Options::COLUMN_WIDTHS,
];
}
/**
* {@inheritdoc}
*/
protected function setDefaultOptions()
{
$this->setOption(Options::TEMP_FOLDER, sys_get_temp_dir());
$this->setOption(Options::DEFAULT_ROW_STYLE, $this->styleBuilder->build());
$this->setOption(Options::SHOULD_CREATE_NEW_SHEETS_AUTOMATICALLY, true);
}
}

View File

@ -1,34 +0,0 @@
<?php
namespace OpenSpout\Writer\ODS;
use OpenSpout\Writer\Common\Entity\Options;
use OpenSpout\Writer\WriterMultiSheetsAbstract;
/**
* This class provides base support to write data to ODS files.
*/
class Writer extends WriterMultiSheetsAbstract
{
/** @var string Content-Type value for the header */
protected static $headerContentType = 'application/vnd.oasis.opendocument.spreadsheet';
/**
* Sets a custom temporary folder for creating intermediate files/folders.
* This must be set before opening the writer.
*
* @param string $tempFolder Temporary folder where the files to create the ODS will be stored
*
* @throws \OpenSpout\Writer\Exception\WriterAlreadyOpenedException If the writer was already opened
*
* @return Writer
*/
public function setTempFolder($tempFolder)
{
$this->throwIfWriterAlreadyOpened('Writer must be configured before opening it.');
$this->optionsManager->setOption(Options::TEMP_FOLDER, $tempFolder);
return $this;
}
}

View File

@ -1,270 +0,0 @@
<?php
namespace OpenSpout\Writer;
use OpenSpout\Common\Creator\HelperFactory;
use OpenSpout\Common\Entity\Row;
use OpenSpout\Common\Entity\Style\Style;
use OpenSpout\Common\Exception\InvalidArgumentException;
use OpenSpout\Common\Exception\IOException;
use OpenSpout\Common\Exception\SpoutException;
use OpenSpout\Common\Helper\GlobalFunctionsHelper;
use OpenSpout\Common\Manager\OptionsManagerInterface;
use OpenSpout\Writer\Common\Entity\Options;
use OpenSpout\Writer\Exception\WriterAlreadyOpenedException;
use OpenSpout\Writer\Exception\WriterNotOpenedException;
abstract class WriterAbstract implements WriterInterface
{
/** @var string Path to the output file */
protected $outputFilePath;
/** @var resource Pointer to the file/stream we will write to */
protected $filePointer;
/** @var bool faut-il garder ouvert le flux quand close() est appelé? */
protected $dontCloseFilePointer = false;
/** @var bool Indicates whether the writer has been opened or not */
protected $isWriterOpened = false;
/** @var GlobalFunctionsHelper Helper to work with global functions */
protected $globalFunctionsHelper;
/** @var HelperFactory */
protected $helperFactory;
/** @var OptionsManagerInterface Writer options manager */
protected $optionsManager;
/** @var string Content-Type value for the header - to be defined by child class */
protected static $headerContentType;
public function __construct(
OptionsManagerInterface $optionsManager,
GlobalFunctionsHelper $globalFunctionsHelper,
HelperFactory $helperFactory
) {
$this->optionsManager = $optionsManager;
$this->globalFunctionsHelper = $globalFunctionsHelper;
$this->helperFactory = $helperFactory;
}
/**
* {@inheritdoc}
*/
public function setDefaultRowStyle(Style $defaultStyle)
{
$this->optionsManager->setOption(Options::DEFAULT_ROW_STYLE, $defaultStyle);
return $this;
}
public function writeToStream($filePointer)
{
$this->outputFilePath = null;
$this->filePointer = $filePointer;
$this->dontCloseFilePointer = true;
$this->throwIfFilePointerIsNotAvailable();
$this->openWriter();
$this->isWriterOpened = true;
return $this;
}
/**
* {@inheritdoc}
*/
public function openToFile($outputFilePath)
{
$this->outputFilePath = $outputFilePath;
$this->filePointer = $this->globalFunctionsHelper->fopen($this->outputFilePath, 'w');
$this->throwIfFilePointerIsNotAvailable();
$this->openWriter();
$this->isWriterOpened = true;
return $this;
}
/**
* @codeCoverageIgnore
* {@inheritdoc}
*/
public function openToBrowser($outputFileName)
{
$this->outputFilePath = $this->globalFunctionsHelper->basename($outputFileName);
$this->filePointer = $this->globalFunctionsHelper->fopen('php://output', 'w');
$this->throwIfFilePointerIsNotAvailable();
// Clear any previous output (otherwise the generated file will be corrupted)
// @see https://github.com/box/spout/issues/241
$this->globalFunctionsHelper->ob_end_clean();
/*
* Set headers
*
* For newer browsers such as Firefox, Chrome, Opera, Safari, etc., they all support and use `filename*`
* specified by the new standard, even if they do not automatically decode filename; it does not matter;
* and for older versions of Internet Explorer, they are not recognized `filename*`, will automatically
* ignore it and use the old `filename` (the only minor flaw is that there must be an English suffix name).
* In this way, the multi-browser multi-language compatibility problem is perfectly solved, which does not
* require UA judgment and is more in line with the standard.
*
* @see https://github.com/box/spout/issues/745
* @see https://tools.ietf.org/html/rfc6266
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition
*/
$this->globalFunctionsHelper->header('Content-Type: '.static::$headerContentType);
$this->globalFunctionsHelper->header(
'Content-Disposition: attachment; '.
'filename="'.rawurlencode($this->outputFilePath).'"; '.
'filename*=UTF-8\'\''.rawurlencode($this->outputFilePath)
);
/*
* When forcing the download of a file over SSL,IE8 and lower browsers fail
* if the Cache-Control and Pragma headers are not set.
*
* @see http://support.microsoft.com/KB/323308
* @see https://github.com/liuggio/ExcelBundle/issues/45
*/
$this->globalFunctionsHelper->header('Cache-Control: max-age=0');
$this->globalFunctionsHelper->header('Pragma: public');
$this->openWriter();
$this->isWriterOpened = true;
return $this;
}
/**
* {@inheritdoc}
*/
public function addRow(Row $row)
{
if ($this->isWriterOpened) {
try {
$this->addRowToWriter($row);
} catch (SpoutException $e) {
// if an exception occurs while writing data,
// close the writer and remove all files created so far.
$this->closeAndAttemptToCleanupAllFiles();
// re-throw the exception to alert developers of the error
throw $e;
}
} else {
throw new WriterNotOpenedException('The writer needs to be opened before adding row.');
}
return $this;
}
/**
* {@inheritdoc}
*/
public function addRows(array $rows)
{
foreach ($rows as $row) {
if (!$row instanceof Row) {
$this->closeAndAttemptToCleanupAllFiles();
throw new InvalidArgumentException('The input should be an array of Row');
}
$this->addRow($row);
}
return $this;
}
/**
* {@inheritdoc}
*/
public function close()
{
if (!$this->isWriterOpened) {
return;
}
$this->closeWriter();
if (!$this->dontCloseFilePointer && \is_resource($this->filePointer)) {
$this->globalFunctionsHelper->fclose($this->filePointer);
}
$this->isWriterOpened = false;
}
/**
* Opens the streamer and makes it ready to accept data.
*
* @throws IOException If the writer cannot be opened
*/
abstract protected function openWriter();
/**
* Adds a row to the currently opened writer.
*
* @param Row $row The row containing cells and styles
*
* @throws WriterNotOpenedException If the workbook is not created yet
* @throws IOException If unable to write data
*/
abstract protected function addRowToWriter(Row $row);
/**
* Closes the streamer, preventing any additional writing.
*/
abstract protected function closeWriter();
/**
* Checks if the pointer to the file/stream to write to is available.
* Will throw an exception if not available.
*
* @throws IOException If the pointer is not available
*/
protected function throwIfFilePointerIsNotAvailable()
{
if (!\is_resource($this->filePointer)) {
throw new IOException('File pointer has not be opened');
}
}
/**
* Checks if the writer has already been opened, since some actions must be done before it gets opened.
* Throws an exception if already opened.
*
* @param string $message Error message
*
* @throws WriterAlreadyOpenedException if the writer was already opened and must not be
*/
protected function throwIfWriterAlreadyOpened($message)
{
if ($this->isWriterOpened) {
throw new WriterAlreadyOpenedException($message);
}
}
/**
* Closes the writer and attempts to cleanup all files that were
* created during the writing process (temp files & final file).
*/
private function closeAndAttemptToCleanupAllFiles()
{
// close the writer, which should remove all temp files
$this->close();
// remove output file if it was created
if ($this->globalFunctionsHelper->file_exists($this->outputFilePath)) {
$outputFolderPath = \dirname($this->outputFilePath);
$fileSystemHelper = $this->helperFactory->createFileSystemHelper($outputFolderPath);
$fileSystemHelper->deleteFile($this->outputFilePath);
}
}
}

View File

@ -1,77 +0,0 @@
<?php
namespace OpenSpout\Writer;
use OpenSpout\Common\Entity\Row;
use OpenSpout\Common\Entity\Style\Style;
/**
* Interface WriterInterface.
*/
interface WriterInterface
{
/**
* Initializes the writer and opens it to accept data.
* By using this method, the data will be written to a file.
*
* @param string $outputFilePath Path of the output file that will contain the data
*
* @throws \OpenSpout\Common\Exception\IOException If the writer cannot be opened or if the given path is not writable
*
* @return WriterInterface
*/
public function openToFile($outputFilePath);
/**
* Initializes the writer and opens it to accept data.
* By using this method, the data will be outputted directly to the browser.
*
* @param string $outputFileName Name of the output file that will contain the data. If a path is passed in, only the file name will be kept
*
* @throws \OpenSpout\Common\Exception\IOException If the writer cannot be opened
*
* @return WriterInterface
*/
public function openToBrowser($outputFileName);
/**
* Sets the default styles for all rows added with "addRow".
* Overriding the default style instead of using "addRowWithStyle" improves performance by 20%.
*
* @see https://github.com/box/spout/issues/272
*
* @return WriterInterface
*/
public function setDefaultRowStyle(Style $defaultStyle);
/**
* Appends a row to the end of the stream.
*
* @param Row $row The row to be appended to the stream
*
* @throws \OpenSpout\Writer\Exception\WriterNotOpenedException If the writer has not been opened yet
* @throws \OpenSpout\Common\Exception\IOException If unable to write data
*
* @return WriterInterface
*/
public function addRow(Row $row);
/**
* Appends the rows to the end of the stream.
*
* @param Row[] $rows The rows to be appended to the stream
*
* @throws \OpenSpout\Common\Exception\InvalidArgumentException If the input param is not valid
* @throws \OpenSpout\Writer\Exception\WriterNotOpenedException If the writer has not been opened yet
* @throws \OpenSpout\Common\Exception\IOException If unable to write data
*
* @return WriterInterface
*/
public function addRows(array $rows);
/**
* Closes the writer. This will close the streamer as well, preventing new data
* to be written to the file.
*/
public function close();
}

View File

@ -1,216 +0,0 @@
<?php
namespace OpenSpout\Writer;
use OpenSpout\Common\Creator\HelperFactory;
use OpenSpout\Common\Entity\Row;
use OpenSpout\Common\Exception\IOException;
use OpenSpout\Common\Helper\GlobalFunctionsHelper;
use OpenSpout\Common\Manager\OptionsManagerInterface;
use OpenSpout\Writer\Common\Creator\ManagerFactoryInterface;
use OpenSpout\Writer\Common\Entity\Options;
use OpenSpout\Writer\Common\Entity\Sheet;
use OpenSpout\Writer\Common\Manager\WorkbookManagerInterface;
use OpenSpout\Writer\Exception\SheetNotFoundException;
use OpenSpout\Writer\Exception\WriterAlreadyOpenedException;
use OpenSpout\Writer\Exception\WriterNotOpenedException;
abstract class WriterMultiSheetsAbstract extends WriterAbstract
{
/** @var ManagerFactoryInterface */
private $managerFactory;
/** @var null|WorkbookManagerInterface */
private $workbookManager;
public function __construct(
OptionsManagerInterface $optionsManager,
GlobalFunctionsHelper $globalFunctionsHelper,
HelperFactory $helperFactory,
ManagerFactoryInterface $managerFactory
) {
parent::__construct($optionsManager, $globalFunctionsHelper, $helperFactory);
$this->managerFactory = $managerFactory;
}
/**
* Sets whether new sheets should be automatically created when the max rows limit per sheet is reached.
* This must be set before opening the writer.
*
* @param bool $shouldCreateNewSheetsAutomatically Whether new sheets should be automatically created when the max rows limit per sheet is reached
*
* @throws WriterAlreadyOpenedException If the writer was already opened
*
* @return WriterMultiSheetsAbstract
*/
public function setShouldCreateNewSheetsAutomatically($shouldCreateNewSheetsAutomatically)
{
$this->throwIfWriterAlreadyOpened('Writer must be configured before opening it.');
$this->optionsManager->setOption(
Options::SHOULD_CREATE_NEW_SHEETS_AUTOMATICALLY,
$shouldCreateNewSheetsAutomatically
);
return $this;
}
/**
* Returns all the workbook's sheets.
*
* @throws WriterNotOpenedException If the writer has not been opened yet
*
* @return Sheet[] All the workbook's sheets
*/
public function getSheets()
{
$this->throwIfWorkbookIsNotAvailable();
$externalSheets = [];
$worksheets = $this->workbookManager->getWorksheets();
foreach ($worksheets as $worksheet) {
$externalSheets[] = $worksheet->getExternalSheet();
}
return $externalSheets;
}
/**
* Creates a new sheet and make it the current sheet. The data will now be written to this sheet.
*
* @throws IOException
* @throws WriterNotOpenedException If the writer has not been opened yet
*
* @return Sheet The created sheet
*/
public function addNewSheetAndMakeItCurrent()
{
$this->throwIfWorkbookIsNotAvailable();
$worksheet = $this->workbookManager->addNewSheetAndMakeItCurrent();
return $worksheet->getExternalSheet();
}
/**
* Returns the current sheet.
*
* @throws WriterNotOpenedException If the writer has not been opened yet
*
* @return Sheet The current sheet
*/
public function getCurrentSheet()
{
$this->throwIfWorkbookIsNotAvailable();
return $this->workbookManager->getCurrentWorksheet()->getExternalSheet();
}
/**
* Sets the given sheet as the current one. New data will be written to this sheet.
* The writing will resume where it stopped (i.e. data won't be truncated).
*
* @param Sheet $sheet The sheet to set as current
*
* @throws SheetNotFoundException If the given sheet does not exist in the workbook
* @throws WriterNotOpenedException If the writer has not been opened yet
*/
public function setCurrentSheet($sheet)
{
$this->throwIfWorkbookIsNotAvailable();
$this->workbookManager->setCurrentSheet($sheet);
}
/**
* @throws WriterAlreadyOpenedException
*/
public function setDefaultColumnWidth(float $width)
{
$this->throwIfWriterAlreadyOpened('Writer must be configured before opening it.');
$this->optionsManager->setOption(
Options::DEFAULT_COLUMN_WIDTH,
$width
);
}
/**
* @throws WriterAlreadyOpenedException
*/
public function setDefaultRowHeight(float $height)
{
$this->throwIfWriterAlreadyOpened('Writer must be configured before opening it.');
$this->optionsManager->setOption(
Options::DEFAULT_ROW_HEIGHT,
$height
);
}
/**
* @param null|float $width
* @param int ...$columns One or more columns with this width
*
* @throws WriterNotOpenedException
*/
public function setColumnWidth($width, ...$columns)
{
$this->throwIfWorkbookIsNotAvailable();
$this->workbookManager->setColumnWidth($width, ...$columns);
}
/**
* @param float $width The width to set
* @param int $start First column index of the range
* @param int $end Last column index of the range
*
* @throws WriterNotOpenedException
*/
public function setColumnWidthForRange(float $width, int $start, int $end)
{
$this->throwIfWorkbookIsNotAvailable();
$this->workbookManager->setColumnWidthForRange($width, $start, $end);
}
/**
* {@inheritdoc}
*/
protected function openWriter()
{
if (null === $this->workbookManager) {
$this->workbookManager = $this->managerFactory->createWorkbookManager($this->optionsManager);
$this->workbookManager->addNewSheetAndMakeItCurrent();
}
}
/**
* Checks if the workbook has been created. Throws an exception if not created yet.
*
* @throws WriterNotOpenedException If the workbook is not created yet
*/
protected function throwIfWorkbookIsNotAvailable()
{
if (!$this->workbookManager->getWorkbook()) {
throw new WriterNotOpenedException('The writer must be opened before performing this action.');
}
}
/**
* {@inheritdoc}
*
* @throws Exception\WriterException
*/
protected function addRowToWriter(Row $row)
{
$this->throwIfWorkbookIsNotAvailable();
$this->workbookManager->addRowToCurrentWorksheet($row);
}
/**
* {@inheritdoc}
*/
protected function closeWriter()
{
if (null !== $this->workbookManager) {
$this->workbookManager->close($this->filePointer);
}
}
}

View File

@ -1,53 +0,0 @@
<?php
namespace OpenSpout\Writer\XLSX\Creator;
use OpenSpout\Common\Helper\Escaper;
use OpenSpout\Common\Helper\StringHelper;
use OpenSpout\Common\Manager\OptionsManagerInterface;
use OpenSpout\Writer\Common\Creator\InternalEntityFactory;
use OpenSpout\Writer\Common\Entity\Options;
use OpenSpout\Writer\Common\Helper\ZipHelper;
use OpenSpout\Writer\XLSX\Helper\FileSystemHelper;
/**
* Factory for helpers needed by the XLSX Writer.
*/
class HelperFactory extends \OpenSpout\Common\Creator\HelperFactory
{
/**
* @return FileSystemHelper
*/
public function createSpecificFileSystemHelper(OptionsManagerInterface $optionsManager, InternalEntityFactory $entityFactory)
{
$tempFolder = $optionsManager->getOption(Options::TEMP_FOLDER);
$zipHelper = $this->createZipHelper($entityFactory);
$escaper = $this->createStringsEscaper();
return new FileSystemHelper($tempFolder, $zipHelper, $escaper);
}
/**
* @return Escaper\XLSX
*/
public function createStringsEscaper()
{
return new Escaper\XLSX();
}
/**
* @return StringHelper
*/
public function createStringHelper()
{
return new StringHelper();
}
/**
* @return ZipHelper
*/
private function createZipHelper(InternalEntityFactory $entityFactory)
{
return new ZipHelper($entityFactory);
}
}

View File

@ -1,145 +0,0 @@
<?php
namespace OpenSpout\Writer\XLSX\Creator;
use OpenSpout\Common\Manager\OptionsManagerInterface;
use OpenSpout\Writer\Common\Creator\InternalEntityFactory;
use OpenSpout\Writer\Common\Creator\ManagerFactoryInterface;
use OpenSpout\Writer\Common\Entity\Options;
use OpenSpout\Writer\Common\Manager\RowManager;
use OpenSpout\Writer\Common\Manager\SheetManager;
use OpenSpout\Writer\Common\Manager\Style\StyleMerger;
use OpenSpout\Writer\XLSX\Manager\SharedStringsManager;
use OpenSpout\Writer\XLSX\Manager\Style\StyleManager;
use OpenSpout\Writer\XLSX\Manager\Style\StyleRegistry;
use OpenSpout\Writer\XLSX\Manager\WorkbookManager;
use OpenSpout\Writer\XLSX\Manager\WorksheetManager;
/**
* Factory for managers needed by the XLSX Writer.
*/
class ManagerFactory implements ManagerFactoryInterface
{
/** @var InternalEntityFactory */
protected $entityFactory;
/** @var HelperFactory */
protected $helperFactory;
public function __construct(InternalEntityFactory $entityFactory, HelperFactory $helperFactory)
{
$this->entityFactory = $entityFactory;
$this->helperFactory = $helperFactory;
}
/**
* @return WorkbookManager
*/
public function createWorkbookManager(OptionsManagerInterface $optionsManager)
{
$workbook = $this->entityFactory->createWorkbook();
$fileSystemHelper = $this->helperFactory->createSpecificFileSystemHelper($optionsManager, $this->entityFactory);
$fileSystemHelper->createBaseFilesAndFolders();
$xlFolder = $fileSystemHelper->getXlFolder();
$sharedStringsManager = $this->createSharedStringsManager($xlFolder);
$styleMerger = $this->createStyleMerger();
$styleManager = $this->createStyleManager($optionsManager);
$worksheetManager = $this->createWorksheetManager($optionsManager, $styleManager, $styleMerger, $sharedStringsManager);
return new WorkbookManager(
$workbook,
$optionsManager,
$worksheetManager,
$styleManager,
$styleMerger,
$fileSystemHelper,
$this->entityFactory,
$this
);
}
/**
* @return SheetManager
*/
public function createSheetManager()
{
$stringHelper = $this->helperFactory->createStringHelper();
return new SheetManager($stringHelper);
}
/**
* @return RowManager
*/
public function createRowManager()
{
return new RowManager();
}
/**
* @return WorksheetManager
*/
private function createWorksheetManager(
OptionsManagerInterface $optionsManager,
StyleManager $styleManager,
StyleMerger $styleMerger,
SharedStringsManager $sharedStringsManager
) {
$rowManager = $this->createRowManager();
$stringsEscaper = $this->helperFactory->createStringsEscaper();
$stringsHelper = $this->helperFactory->createStringHelper();
return new WorksheetManager(
$optionsManager,
$rowManager,
$styleManager,
$styleMerger,
$sharedStringsManager,
$stringsEscaper,
$stringsHelper
);
}
/**
* @return StyleManager
*/
private function createStyleManager(OptionsManagerInterface $optionsManager)
{
$styleRegistry = $this->createStyleRegistry($optionsManager);
return new StyleManager($styleRegistry);
}
/**
* @return StyleRegistry
*/
private function createStyleRegistry(OptionsManagerInterface $optionsManager)
{
$defaultRowStyle = $optionsManager->getOption(Options::DEFAULT_ROW_STYLE);
return new StyleRegistry($defaultRowStyle);
}
/**
* @return StyleMerger
*/
private function createStyleMerger()
{
return new StyleMerger();
}
/**
* @param string $xlFolder Path to the "xl" folder
*
* @return SharedStringsManager
*/
private function createSharedStringsManager($xlFolder)
{
$stringEscaper = $this->helperFactory->createStringsEscaper();
return new SharedStringsManager($xlFolder, $stringEscaper);
}
}

View File

@ -1,403 +0,0 @@
<?php
namespace OpenSpout\Writer\XLSX\Helper;
use OpenSpout\Writer\Common\Entity\Worksheet;
use OpenSpout\Writer\Common\Helper\FileSystemWithRootFolderHelperInterface;
use OpenSpout\Writer\Common\Helper\ZipHelper;
use OpenSpout\Writer\XLSX\Manager\Style\StyleManager;
/**
* This class provides helper functions to help with the file system operations
* like files/folders creation & deletion for XLSX files.
*/
class FileSystemHelper extends \OpenSpout\Common\Helper\FileSystemHelper implements FileSystemWithRootFolderHelperInterface
{
public const APP_NAME = 'Spout';
public const RELS_FOLDER_NAME = '_rels';
public const DOC_PROPS_FOLDER_NAME = 'docProps';
public const XL_FOLDER_NAME = 'xl';
public const WORKSHEETS_FOLDER_NAME = 'worksheets';
public const RELS_FILE_NAME = '.rels';
public const APP_XML_FILE_NAME = 'app.xml';
public const CORE_XML_FILE_NAME = 'core.xml';
public const CONTENT_TYPES_XML_FILE_NAME = '[Content_Types].xml';
public const WORKBOOK_XML_FILE_NAME = 'workbook.xml';
public const WORKBOOK_RELS_XML_FILE_NAME = 'workbook.xml.rels';
public const STYLES_XML_FILE_NAME = 'styles.xml';
/** @var ZipHelper Helper to perform tasks with Zip archive */
private $zipHelper;
/** @var \OpenSpout\Common\Helper\Escaper\XLSX Used to escape XML data */
private $escaper;
/** @var string Path to the root folder inside the temp folder where the files to create the XLSX will be stored */
private $rootFolder;
/** @var string Path to the "_rels" folder inside the root folder */
private $relsFolder;
/** @var string Path to the "docProps" folder inside the root folder */
private $docPropsFolder;
/** @var string Path to the "xl" folder inside the root folder */
private $xlFolder;
/** @var string Path to the "_rels" folder inside the "xl" folder */
private $xlRelsFolder;
/** @var string Path to the "worksheets" folder inside the "xl" folder */
private $xlWorksheetsFolder;
/**
* @param string $baseFolderPath The path of the base folder where all the I/O can occur
* @param ZipHelper $zipHelper Helper to perform tasks with Zip archive
* @param \OpenSpout\Common\Helper\Escaper\XLSX $escaper Used to escape XML data
*/
public function __construct($baseFolderPath, $zipHelper, $escaper)
{
parent::__construct($baseFolderPath);
$this->zipHelper = $zipHelper;
$this->escaper = $escaper;
}
/**
* @return string
*/
public function getRootFolder()
{
return $this->rootFolder;
}
/**
* @return string
*/
public function getXlFolder()
{
return $this->xlFolder;
}
/**
* @return string
*/
public function getXlWorksheetsFolder()
{
return $this->xlWorksheetsFolder;
}
/**
* Creates all the folders needed to create a XLSX file, as well as the files that won't change.
*
* @throws \OpenSpout\Common\Exception\IOException If unable to create at least one of the base folders
*/
public function createBaseFilesAndFolders()
{
$this
->createRootFolder()
->createRelsFolderAndFile()
->createDocPropsFolderAndFiles()
->createXlFolderAndSubFolders()
;
}
/**
* Creates the "[Content_Types].xml" file under the root folder.
*
* @param Worksheet[] $worksheets
*
* @return FileSystemHelper
*/
public function createContentTypesFile($worksheets)
{
$contentTypesXmlFileContents = <<<'EOD'
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
<Default ContentType="application/xml" Extension="xml"/>
<Default ContentType="application/vnd.openxmlformats-package.relationships+xml" Extension="rels"/>
<Override ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml" PartName="/xl/workbook.xml"/>
EOD;
/** @var Worksheet $worksheet */
foreach ($worksheets as $worksheet) {
$contentTypesXmlFileContents .= '<Override ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml" PartName="/xl/worksheets/sheet'.$worksheet->getId().'.xml"/>';
}
$contentTypesXmlFileContents .= <<<'EOD'
<Override ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml" PartName="/xl/styles.xml"/>
<Override ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml" PartName="/xl/sharedStrings.xml"/>
<Override ContentType="application/vnd.openxmlformats-package.core-properties+xml" PartName="/docProps/core.xml"/>
<Override ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml" PartName="/docProps/app.xml"/>
</Types>
EOD;
$this->createFileWithContents($this->rootFolder, self::CONTENT_TYPES_XML_FILE_NAME, $contentTypesXmlFileContents);
return $this;
}
/**
* Creates the "workbook.xml" file under the "xl" folder.
*
* @param Worksheet[] $worksheets
*
* @return FileSystemHelper
*/
public function createWorkbookFile($worksheets)
{
$workbookXmlFileContents = <<<'EOD'
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
<sheets>
EOD;
/** @var Worksheet $worksheet */
foreach ($worksheets as $worksheet) {
$worksheetName = $worksheet->getExternalSheet()->getName();
$worksheetVisibility = $worksheet->getExternalSheet()->isVisible() ? 'visible' : 'hidden';
$worksheetId = $worksheet->getId();
$workbookXmlFileContents .= '<sheet name="'.$this->escaper->escape($worksheetName).'" sheetId="'.$worksheetId.'" r:id="rIdSheet'.$worksheetId.'" state="'.$worksheetVisibility.'"/>';
}
$workbookXmlFileContents .= <<<'EOD'
</sheets>
</workbook>
EOD;
$this->createFileWithContents($this->xlFolder, self::WORKBOOK_XML_FILE_NAME, $workbookXmlFileContents);
return $this;
}
/**
* Creates the "workbook.xml.res" file under the "xl/_res" folder.
*
* @param Worksheet[] $worksheets
*
* @return FileSystemHelper
*/
public function createWorkbookRelsFile($worksheets)
{
$workbookRelsXmlFileContents = <<<'EOD'
<?xml version="1.0" encoding="UTF-8"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="rIdStyles" Target="styles.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles"/>
<Relationship Id="rIdSharedStrings" Target="sharedStrings.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings"/>
EOD;
/** @var Worksheet $worksheet */
foreach ($worksheets as $worksheet) {
$worksheetId = $worksheet->getId();
$workbookRelsXmlFileContents .= '<Relationship Id="rIdSheet'.$worksheetId.'" Target="worksheets/sheet'.$worksheetId.'.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet"/>';
}
$workbookRelsXmlFileContents .= '</Relationships>';
$this->createFileWithContents($this->xlRelsFolder, self::WORKBOOK_RELS_XML_FILE_NAME, $workbookRelsXmlFileContents);
return $this;
}
/**
* Creates the "styles.xml" file under the "xl" folder.
*
* @param StyleManager $styleManager
*
* @return FileSystemHelper
*/
public function createStylesFile($styleManager)
{
$stylesXmlFileContents = $styleManager->getStylesXMLFileContent();
$this->createFileWithContents($this->xlFolder, self::STYLES_XML_FILE_NAME, $stylesXmlFileContents);
return $this;
}
/**
* Zips the root folder and streams the contents of the zip into the given stream.
*
* @param resource $streamPointer Pointer to the stream to copy the zip
*/
public function zipRootFolderAndCopyToStream($streamPointer)
{
$zip = $this->zipHelper->createZip($this->rootFolder);
$zipFilePath = $this->zipHelper->getZipFilePath($zip);
// In order to have the file's mime type detected properly, files need to be added
// to the zip file in a particular order.
// "[Content_Types].xml" then at least 2 files located in "xl" folder should be zipped first.
$this->zipHelper->addFileToArchive($zip, $this->rootFolder, self::CONTENT_TYPES_XML_FILE_NAME);
$this->zipHelper->addFileToArchive($zip, $this->rootFolder, self::XL_FOLDER_NAME.'/'.self::WORKBOOK_XML_FILE_NAME);
$this->zipHelper->addFileToArchive($zip, $this->rootFolder, self::XL_FOLDER_NAME.'/'.self::STYLES_XML_FILE_NAME);
$this->zipHelper->addFolderToArchive($zip, $this->rootFolder, ZipHelper::EXISTING_FILES_SKIP);
$this->zipHelper->closeArchiveAndCopyToStream($zip, $streamPointer);
// once the zip is copied, remove it
$this->deleteFile($zipFilePath);
}
/**
* Creates the folder that will be used as root.
*
* @throws \OpenSpout\Common\Exception\IOException If unable to create the folder
*
* @return FileSystemHelper
*/
private function createRootFolder()
{
$this->rootFolder = $this->createFolder($this->baseFolderRealPath, uniqid('xlsx', true));
return $this;
}
/**
* Creates the "_rels" folder under the root folder as well as the ".rels" file in it.
*
* @throws \OpenSpout\Common\Exception\IOException If unable to create the folder or the ".rels" file
*
* @return FileSystemHelper
*/
private function createRelsFolderAndFile()
{
$this->relsFolder = $this->createFolder($this->rootFolder, self::RELS_FOLDER_NAME);
$this->createRelsFile();
return $this;
}
/**
* Creates the ".rels" file under the "_rels" folder (under root).
*
* @throws \OpenSpout\Common\Exception\IOException If unable to create the file
*
* @return FileSystemHelper
*/
private function createRelsFile()
{
$relsFileContents = <<<'EOD'
<?xml version="1.0" encoding="UTF-8"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="rIdWorkbook" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>
<Relationship Id="rIdCore" Type="http://schemas.openxmlformats.org/officedocument/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/>
<Relationship Id="rIdApp" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/>
</Relationships>
EOD;
$this->createFileWithContents($this->relsFolder, self::RELS_FILE_NAME, $relsFileContents);
return $this;
}
/**
* Creates the "docProps" folder under the root folder as well as the "app.xml" and "core.xml" files in it.
*
* @throws \OpenSpout\Common\Exception\IOException If unable to create the folder or one of the files
*
* @return FileSystemHelper
*/
private function createDocPropsFolderAndFiles()
{
$this->docPropsFolder = $this->createFolder($this->rootFolder, self::DOC_PROPS_FOLDER_NAME);
$this->createAppXmlFile();
$this->createCoreXmlFile();
return $this;
}
/**
* Creates the "app.xml" file under the "docProps" folder.
*
* @throws \OpenSpout\Common\Exception\IOException If unable to create the file
*
* @return FileSystemHelper
*/
private function createAppXmlFile()
{
$appName = self::APP_NAME;
$appXmlFileContents = <<<EOD
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties">
<Application>{$appName}</Application>
<TotalTime>0</TotalTime>
</Properties>
EOD;
$this->createFileWithContents($this->docPropsFolder, self::APP_XML_FILE_NAME, $appXmlFileContents);
return $this;
}
/**
* Creates the "core.xml" file under the "docProps" folder.
*
* @throws \OpenSpout\Common\Exception\IOException If unable to create the file
*
* @return FileSystemHelper
*/
private function createCoreXmlFile()
{
$createdDate = (new \DateTime())->format(\DateTime::W3C);
$coreXmlFileContents = <<<EOD
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<dcterms:created xsi:type="dcterms:W3CDTF">{$createdDate}</dcterms:created>
<dcterms:modified xsi:type="dcterms:W3CDTF">{$createdDate}</dcterms:modified>
<cp:revision>0</cp:revision>
</cp:coreProperties>
EOD;
$this->createFileWithContents($this->docPropsFolder, self::CORE_XML_FILE_NAME, $coreXmlFileContents);
return $this;
}
/**
* Creates the "xl" folder under the root folder as well as its subfolders.
*
* @throws \OpenSpout\Common\Exception\IOException If unable to create at least one of the folders
*
* @return FileSystemHelper
*/
private function createXlFolderAndSubFolders()
{
$this->xlFolder = $this->createFolder($this->rootFolder, self::XL_FOLDER_NAME);
$this->createXlRelsFolder();
$this->createXlWorksheetsFolder();
return $this;
}
/**
* Creates the "_rels" folder under the "xl" folder.
*
* @throws \OpenSpout\Common\Exception\IOException If unable to create the folder
*
* @return FileSystemHelper
*/
private function createXlRelsFolder()
{
$this->xlRelsFolder = $this->createFolder($this->xlFolder, self::RELS_FOLDER_NAME);
return $this;
}
/**
* Creates the "worksheets" folder under the "xl" folder.
*
* @throws \OpenSpout\Common\Exception\IOException If unable to create the folder
*
* @return FileSystemHelper
*/
private function createXlWorksheetsFolder()
{
$this->xlWorksheetsFolder = $this->createFolder($this->xlFolder, self::WORKSHEETS_FOLDER_NAME);
return $this;
}
}

View File

@ -1,64 +0,0 @@
<?php
namespace OpenSpout\Writer\XLSX\Manager;
use OpenSpout\Common\Manager\OptionsManagerAbstract;
use OpenSpout\Writer\Common\Creator\Style\StyleBuilder;
use OpenSpout\Writer\Common\Entity\Options;
/**
* XLSX Writer options manager.
*/
class OptionsManager extends OptionsManagerAbstract
{
/** Default style font values */
public const DEFAULT_FONT_SIZE = 12;
public const DEFAULT_FONT_NAME = 'Calibri';
/** @var StyleBuilder Style builder */
protected $styleBuilder;
/**
* OptionsManager constructor.
*/
public function __construct(StyleBuilder $styleBuilder)
{
$this->styleBuilder = $styleBuilder;
parent::__construct();
}
/**
* {@inheritdoc}
*/
protected function getSupportedOptions()
{
return [
Options::TEMP_FOLDER,
Options::DEFAULT_ROW_STYLE,
Options::SHOULD_CREATE_NEW_SHEETS_AUTOMATICALLY,
Options::SHOULD_USE_INLINE_STRINGS,
Options::DEFAULT_COLUMN_WIDTH,
Options::DEFAULT_ROW_HEIGHT,
Options::COLUMN_WIDTHS,
Options::MERGE_CELLS,
];
}
/**
* {@inheritdoc}
*/
protected function setDefaultOptions()
{
$defaultRowStyle = $this->styleBuilder
->setFontSize(self::DEFAULT_FONT_SIZE)
->setFontName(self::DEFAULT_FONT_NAME)
->build()
;
$this->setOption(Options::TEMP_FOLDER, sys_get_temp_dir());
$this->setOption(Options::DEFAULT_ROW_STYLE, $defaultRowStyle);
$this->setOption(Options::SHOULD_CREATE_NEW_SHEETS_AUTOMATICALLY, true);
$this->setOption(Options::SHOULD_USE_INLINE_STRINGS, true);
$this->setOption(Options::MERGE_CELLS, []);
}
}

View File

@ -1,74 +0,0 @@
<?php
namespace OpenSpout\Writer\XLSX\Manager;
use OpenSpout\Writer\Common\Entity\Sheet;
use OpenSpout\Writer\Common\Manager\WorkbookManagerAbstract;
use OpenSpout\Writer\XLSX\Helper\FileSystemHelper;
use OpenSpout\Writer\XLSX\Manager\Style\StyleManager;
/**
* XLSX workbook manager, providing the interfaces to work with workbook.
*/
class WorkbookManager extends WorkbookManagerAbstract
{
/**
* Maximum number of rows a XLSX sheet can contain.
*
* @see http://office.microsoft.com/en-us/excel-help/excel-specifications-and-limits-HP010073849.aspx
*/
protected static $maxRowsPerWorksheet = 1048576;
/** @var WorksheetManager Object used to manage worksheets */
protected $worksheetManager;
/** @var StyleManager Manages styles */
protected $styleManager;
/** @var FileSystemHelper Helper to perform file system operations */
protected $fileSystemHelper;
/**
* @return string The file path where the data for the given sheet will be stored
*/
public function getWorksheetFilePath(Sheet $sheet)
{
$worksheetFilesFolder = $this->fileSystemHelper->getXlWorksheetsFolder();
return $worksheetFilesFolder.'/'.strtolower($sheet->getName()).'.xml';
}
/**
* @return int Maximum number of rows/columns a sheet can contain
*/
protected function getMaxRowsPerWorksheet()
{
return self::$maxRowsPerWorksheet;
}
/**
* Closes custom objects that are still opened.
*/
protected function closeRemainingObjects()
{
$this->worksheetManager->getSharedStringsManager()->close();
}
/**
* Writes all the necessary files to disk and zip them together to create the final file.
*
* @param resource $finalFilePointer Pointer to the spreadsheet that will be created
*/
protected function writeAllFilesToDiskAndZipThem($finalFilePointer)
{
$worksheets = $this->getWorksheets();
$this->fileSystemHelper
->createContentTypesFile($worksheets)
->createWorkbookFile($worksheets)
->createWorkbookRelsFile($worksheets)
->createStylesFile($this->styleManager)
->zipRootFolderAndCopyToStream($finalFilePointer)
;
}
}

View File

@ -1,376 +0,0 @@
<?php
namespace OpenSpout\Writer\XLSX\Manager;
use OpenSpout\Common\Entity\Cell;
use OpenSpout\Common\Entity\Row;
use OpenSpout\Common\Entity\Style\Style;
use OpenSpout\Common\Exception\InvalidArgumentException;
use OpenSpout\Common\Exception\IOException;
use OpenSpout\Common\Helper\Escaper\XLSX as XLSXEscaper;
use OpenSpout\Common\Helper\StringHelper;
use OpenSpout\Common\Manager\OptionsManagerInterface;
use OpenSpout\Writer\Common\Entity\Options;
use OpenSpout\Writer\Common\Entity\Worksheet;
use OpenSpout\Writer\Common\Helper\CellHelper;
use OpenSpout\Writer\Common\Manager\ManagesCellSize;
use OpenSpout\Writer\Common\Manager\RegisteredStyle;
use OpenSpout\Writer\Common\Manager\RowManager;
use OpenSpout\Writer\Common\Manager\Style\StyleMerger;
use OpenSpout\Writer\Common\Manager\WorksheetManagerInterface;
use OpenSpout\Writer\XLSX\Helper\DateHelper;
use OpenSpout\Writer\XLSX\Manager\Style\StyleManager;
/**
* XLSX worksheet manager, providing the interfaces to work with XLSX worksheets.
*/
class WorksheetManager implements WorksheetManagerInterface
{
use ManagesCellSize;
/**
* Maximum number of characters a cell can contain.
*
* @see https://support.office.com/en-us/article/Excel-specifications-and-limits-16c69c74-3d6a-4aaf-ba35-e6eb276e8eaa [Excel 2007]
* @see https://support.office.com/en-us/article/Excel-specifications-and-limits-1672b34d-7043-467e-8e27-269d656771c3 [Excel 2010]
* @see https://support.office.com/en-us/article/Excel-specifications-and-limits-ca36e2dc-1f09-4620-b726-67c00b05040f [Excel 2013/2016]
*/
public const MAX_CHARACTERS_PER_CELL = 32767;
public const SHEET_XML_FILE_HEADER = <<<'EOD'
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
EOD;
/** @var bool Whether inline or shared strings should be used */
protected $shouldUseInlineStrings;
/** @var OptionsManagerInterface */
private $optionsManager;
/** @var RowManager Manages rows */
private $rowManager;
/** @var StyleManager Manages styles */
private $styleManager;
/** @var StyleMerger Helper to merge styles together */
private $styleMerger;
/** @var SharedStringsManager Helper to write shared strings */
private $sharedStringsManager;
/** @var XLSXEscaper Strings escaper */
private $stringsEscaper;
/** @var StringHelper String helper */
private $stringHelper;
/**
* WorksheetManager constructor.
*/
public function __construct(
OptionsManagerInterface $optionsManager,
RowManager $rowManager,
StyleManager $styleManager,
StyleMerger $styleMerger,
SharedStringsManager $sharedStringsManager,
XLSXEscaper $stringsEscaper,
StringHelper $stringHelper
) {
$this->optionsManager = $optionsManager;
$this->shouldUseInlineStrings = $optionsManager->getOption(Options::SHOULD_USE_INLINE_STRINGS);
$this->setDefaultColumnWidth($optionsManager->getOption(Options::DEFAULT_COLUMN_WIDTH));
$this->setDefaultRowHeight($optionsManager->getOption(Options::DEFAULT_ROW_HEIGHT));
$this->columnWidths = $optionsManager->getOption(Options::COLUMN_WIDTHS) ?? [];
$this->rowManager = $rowManager;
$this->styleManager = $styleManager;
$this->styleMerger = $styleMerger;
$this->sharedStringsManager = $sharedStringsManager;
$this->stringsEscaper = $stringsEscaper;
$this->stringHelper = $stringHelper;
}
/**
* @return SharedStringsManager
*/
public function getSharedStringsManager()
{
return $this->sharedStringsManager;
}
/**
* {@inheritdoc}
*/
public function startSheet(Worksheet $worksheet)
{
$sheetFilePointer = fopen($worksheet->getFilePath(), 'w');
$this->throwIfSheetFilePointerIsNotAvailable($sheetFilePointer);
$worksheet->setFilePointer($sheetFilePointer);
fwrite($sheetFilePointer, self::SHEET_XML_FILE_HEADER);
}
/**
* {@inheritdoc}
*/
public function addRow(Worksheet $worksheet, Row $row)
{
if (!$this->rowManager->isEmpty($row)) {
$this->addNonEmptyRow($worksheet, $row);
}
$worksheet->setLastWrittenRowIndex($worksheet->getLastWrittenRowIndex() + 1);
}
/**
* Construct column width references xml to inject into worksheet xml file.
*
* @return string
*/
public function getXMLFragmentForColumnWidths()
{
if (empty($this->columnWidths)) {
return '';
}
$xml = '<cols>';
foreach ($this->columnWidths as $entry) {
$xml .= '<col min="'.$entry[0].'" max="'.$entry[1].'" width="'.$entry[2].'" customWidth="true"/>';
}
$xml .= '</cols>';
return $xml;
}
/**
* Constructs default row height and width xml to inject into worksheet xml file.
*
* @return string
*/
public function getXMLFragmentForDefaultCellSizing()
{
$rowHeightXml = empty($this->defaultRowHeight) ? '' : " defaultRowHeight=\"{$this->defaultRowHeight}\"";
$colWidthXml = empty($this->defaultColumnWidth) ? '' : " defaultColWidth=\"{$this->defaultColumnWidth}\"";
if (empty($colWidthXml) && empty($rowHeightXml)) {
return '';
}
// Ensure that the required defaultRowHeight is set
$rowHeightXml = empty($rowHeightXml) ? ' defaultRowHeight="0"' : $rowHeightXml;
return "<sheetFormatPr{$colWidthXml}{$rowHeightXml}/>";
}
/**
* {@inheritdoc}
*/
public function close(Worksheet $worksheet)
{
$worksheetFilePointer = $worksheet->getFilePointer();
if (!\is_resource($worksheetFilePointer)) {
return;
}
$this->ensureSheetDataStated($worksheet);
fwrite($worksheetFilePointer, '</sheetData>');
// create nodes for merge cells
if ($this->optionsManager->getOption(Options::MERGE_CELLS)) {
$mergeCellString = '<mergeCells count="'.\count($this->optionsManager->getOption(Options::MERGE_CELLS)).'">';
foreach ($this->optionsManager->getOption(Options::MERGE_CELLS) as $values) {
$output = array_map(function ($value) {
return CellHelper::getColumnLettersFromColumnIndex($value[0]).$value[1];
}, $values);
$mergeCellString .= '<mergeCell ref="'.implode(':', $output).'"/>';
}
$mergeCellString .= '</mergeCells>';
fwrite($worksheet->getFilePointer(), $mergeCellString);
}
fwrite($worksheetFilePointer, '</worksheet>');
fclose($worksheetFilePointer);
}
/**
* Writes the sheet data header.
*
* @param Worksheet $worksheet The worksheet to add the row to
*/
private function ensureSheetDataStated(Worksheet $worksheet)
{
if (!$worksheet->getSheetDataStarted()) {
$worksheetFilePointer = $worksheet->getFilePointer();
$sheet = $worksheet->getExternalSheet();
if ($sheet->hasSheetView()) {
fwrite($worksheetFilePointer, '<sheetViews>'.$sheet->getSheetView()->getXml().'</sheetViews>');
}
fwrite($worksheetFilePointer, $this->getXMLFragmentForDefaultCellSizing());
fwrite($worksheetFilePointer, $this->getXMLFragmentForColumnWidths());
fwrite($worksheetFilePointer, '<sheetData>');
$worksheet->setSheetDataStarted(true);
}
}
/**
* Checks if the sheet has been sucessfully created. Throws an exception if not.
*
* @param bool|resource $sheetFilePointer Pointer to the sheet data file or FALSE if unable to open the file
*
* @throws IOException If the sheet data file cannot be opened for writing
*/
private function throwIfSheetFilePointerIsNotAvailable($sheetFilePointer)
{
if (!$sheetFilePointer) {
throw new IOException('Unable to open sheet for writing.');
}
}
/**
* Adds non empty row to the worksheet.
*
* @param Worksheet $worksheet The worksheet to add the row to
* @param Row $row The row to be written
*
* @throws InvalidArgumentException If a cell value's type is not supported
* @throws IOException If the data cannot be written
*/
private function addNonEmptyRow(Worksheet $worksheet, Row $row)
{
$this->ensureSheetDataStated($worksheet);
$sheetFilePointer = $worksheet->getFilePointer();
$rowStyle = $row->getStyle();
$rowIndexOneBased = $worksheet->getLastWrittenRowIndex() + 1;
$numCells = $row->getNumCells();
$hasCustomHeight = $this->defaultRowHeight > 0 ? '1' : '0';
$rowXML = "<row r=\"{$rowIndexOneBased}\" spans=\"1:{$numCells}\" customHeight=\"{$hasCustomHeight}\">";
foreach ($row->getCells() as $columnIndexZeroBased => $cell) {
$registeredStyle = $this->applyStyleAndRegister($cell, $rowStyle);
$cellStyle = $registeredStyle->getStyle();
if ($registeredStyle->isMatchingRowStyle()) {
$rowStyle = $cellStyle; // Replace actual rowStyle (possibly with null id) by registered style (with id)
}
$rowXML .= $this->getCellXML($rowIndexOneBased, $columnIndexZeroBased, $cell, $cellStyle->getId());
}
$rowXML .= '</row>';
$wasWriteSuccessful = fwrite($sheetFilePointer, $rowXML);
if (false === $wasWriteSuccessful) {
throw new IOException("Unable to write data in {$worksheet->getFilePath()}");
}
}
/**
* Applies styles to the given style, merging the cell's style with its row's style.
*
* @throws InvalidArgumentException If the given value cannot be processed
*/
private function applyStyleAndRegister(Cell $cell, Style $rowStyle): RegisteredStyle
{
$isMatchingRowStyle = false;
if ($cell->getStyle()->isEmpty()) {
$cell->setStyle($rowStyle);
$possiblyUpdatedStyle = $this->styleManager->applyExtraStylesIfNeeded($cell);
if ($possiblyUpdatedStyle->isUpdated()) {
$registeredStyle = $this->styleManager->registerStyle($possiblyUpdatedStyle->getStyle());
} else {
$registeredStyle = $this->styleManager->registerStyle($rowStyle);
$isMatchingRowStyle = true;
}
} else {
$mergedCellAndRowStyle = $this->styleMerger->merge($cell->getStyle(), $rowStyle);
$cell->setStyle($mergedCellAndRowStyle);
$possiblyUpdatedStyle = $this->styleManager->applyExtraStylesIfNeeded($cell);
if ($possiblyUpdatedStyle->isUpdated()) {
$newCellStyle = $possiblyUpdatedStyle->getStyle();
} else {
$newCellStyle = $mergedCellAndRowStyle;
}
$registeredStyle = $this->styleManager->registerStyle($newCellStyle);
}
return new RegisteredStyle($registeredStyle, $isMatchingRowStyle);
}
/**
* Builds and returns xml for a single cell.
*
* @param int $rowIndexOneBased
* @param int $columnIndexZeroBased
* @param int $styleId
*
* @throws InvalidArgumentException If the given value cannot be processed
*
* @return string
*/
private function getCellXML($rowIndexOneBased, $columnIndexZeroBased, Cell $cell, $styleId)
{
$columnLetters = CellHelper::getColumnLettersFromColumnIndex($columnIndexZeroBased);
$cellXML = '<c r="'.$columnLetters.$rowIndexOneBased.'"';
$cellXML .= ' s="'.$styleId.'"';
if ($cell->isString()) {
$cellXML .= $this->getCellXMLFragmentForNonEmptyString($cell->getValue());
} elseif ($cell->isBoolean()) {
$cellXML .= ' t="b"><v>'.(int) ($cell->getValue()).'</v></c>';
} elseif ($cell->isNumeric()) {
$cellXML .= '><v>'.$this->stringHelper->formatNumericValue($cell->getValue()).'</v></c>';
} elseif ($cell->isFormula()) {
$cellXML .= '><f>'.substr($cell->getValue(), 1).'</f></c>';
} elseif ($cell->isDate()) {
$value = $cell->getValue();
if ($value instanceof \DateTimeInterface) {
$cellXML .= '><v>'.(string) DateHelper::toExcel($value).'</v></c>';
} else {
throw new InvalidArgumentException('Trying to add a date value with an unsupported type: '.\gettype($value));
}
} elseif ($cell->isError() && \is_string($cell->getValueEvenIfError())) {
// only writes the error value if it's a string
$cellXML .= ' t="e"><v>'.$cell->getValueEvenIfError().'</v></c>';
} elseif ($cell->isEmpty()) {
if ($this->styleManager->shouldApplyStyleOnEmptyCell($styleId)) {
$cellXML .= '/>';
} else {
// don't write empty cells that do no need styling
// NOTE: not appending to $cellXML is the right behavior!!
$cellXML = '';
}
} else {
throw new InvalidArgumentException('Trying to add a value with an unsupported type: '.\gettype($cell->getValue()));
}
return $cellXML;
}
/**
* Returns the XML fragment for a cell containing a non empty string.
*
* @param string $cellValue The cell value
*
* @throws InvalidArgumentException If the string exceeds the maximum number of characters allowed per cell
*
* @return string The XML fragment representing the cell
*/
private function getCellXMLFragmentForNonEmptyString($cellValue)
{
if ($this->stringHelper->getStringLength($cellValue) > self::MAX_CHARACTERS_PER_CELL) {
throw new InvalidArgumentException('Trying to add a value that exceeds the maximum number of characters allowed in a cell (32,767)');
}
if ($this->shouldUseInlineStrings) {
$cellXMLFragment = ' t="inlineStr"><is><t>'.$this->stringsEscaper->escape($cellValue).'</t></is></c>';
} else {
$sharedStringId = $this->sharedStringsManager->writeString($cellValue);
$cellXMLFragment = ' t="s"><v>'.$sharedStringId.'</v></c>';
}
return $cellXMLFragment;
}
}

View File

@ -1,72 +0,0 @@
<?php
namespace OpenSpout\Writer\XLSX;
use OpenSpout\Writer\Common\Entity\Options;
use OpenSpout\Writer\WriterMultiSheetsAbstract;
/**
* This class provides base support to write data to XLSX files.
*/
class Writer extends WriterMultiSheetsAbstract
{
/** @var string Content-Type value for the header */
protected static $headerContentType = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
/**
* Sets a custom temporary folder for creating intermediate files/folders.
* This must be set before opening the writer.
*
* @param string $tempFolder Temporary folder where the files to create the XLSX will be stored
*
* @throws \OpenSpout\Writer\Exception\WriterAlreadyOpenedException If the writer was already opened
*
* @return Writer
*/
public function setTempFolder($tempFolder)
{
$this->throwIfWriterAlreadyOpened('Writer must be configured before opening it.');
$this->optionsManager->setOption(Options::TEMP_FOLDER, $tempFolder);
return $this;
}
/**
* Use inline string to be more memory efficient. If set to false, it will use shared strings.
* This must be set before opening the writer.
*
* @param bool $shouldUseInlineStrings Whether inline or shared strings should be used
*
* @throws \OpenSpout\Writer\Exception\WriterAlreadyOpenedException If the writer was already opened
*
* @return Writer
*/
public function setShouldUseInlineStrings($shouldUseInlineStrings)
{
$this->throwIfWriterAlreadyOpened('Writer must be configured before opening it.');
$this->optionsManager->setOption(Options::SHOULD_USE_INLINE_STRINGS, $shouldUseInlineStrings);
return $this;
}
/**
* Merge cells.
* Row coordinates are indexed from 1, columns from 0 (A = 0),
* so a merge B2:G2 looks like $writer->mergeCells([1,2], [6, 2]);.
*
* You may use CellHelper::getColumnLettersFromColumnIndex() to convert from "B2" to "[1,2]"
*
* @param int[] $range1 - top left cell's coordinate [column, row]
* @param int[] $range2 - bottom right cell's coordinate [column, row]
*
* @return $this
*/
public function mergeCells(array $range1, array $range2)
{
$this->optionsManager->addOption(Options::MERGE_CELLS, [$range1, $range2]);
return $this;
}
}

38
upstream-4.x/README.md Normal file
View File

@ -0,0 +1,38 @@
# OpenSpout
[![Latest Stable Version](https://poser.pugx.org/openspout/openspout/v/stable)](https://packagist.org/packages/openspout/openspout)
[![Total Downloads](https://poser.pugx.org/openspout/openspout/downloads)](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)
[![Infection MSI](https://img.shields.io/endpoint?style=flat&url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2Fopenspout%2Fopenspout%2F4.x)](https://dashboard.stryker-mutator.io/reports/github.com/openspout/openspout/4.x)
OpenSpout is a community driven fork of `box/spout`, a PHP library to read and write spreadsheet files
(CSV, XLSX and ODS), in a fast and scalable way. Unlike other file readers or writers, it is capable of processing
very large files, while keeping the memory usage really low (less than 3MB).
## Documentation
Documentation can be found at [`docs/`](docs).
## Upgrade from `box/spout:v3` to `openspout/openspout:v3`
1. Replace `box/spout` with `openspout/openspout` in your `composer.json`
2. Replace `Box\Spout` with `OpenSpout` in your code
## Upgrade guide
Version 4 introduced new functionality but also some breaking changes. If you want to upgrade your OpenSpout codebase
please consult the [Upgrade guide](UPGRADE.md).
## Copyright and License
This is a fork of Box's Spout library: https://github.com/box/spout
Code until and directly descending from commit [`cc42c1d`](https://github.com/openspout/openspout/commit/cc42c1d29fc5d29f07caeace99bd29dbb6d7c2f8)
is copyright of _Box, Inc._ and licensed under the Apache License, Version 2.0:
https://github.com/openspout/openspout/blob/cc42c1d29fc5d29f07caeace99bd29dbb6d7c2f8/LICENSE
Code created, edited and released after the commit mentioned above
is copyright of _openspout_ Github organization and licensed under MIT License.
https://github.com/openspout/openspout/blob/main/LICENSE

View File

@ -1,33 +1,103 @@
Upgrading from 2.x to 3.0 # Upgrade guide
=========================
Spout 3.0 introduced several backwards-incompatible changes. The upgrade from Spout 2.x to 3.0 must therefore be done with caution. ## Upgrading from 3.x to 4.0
Beginning with v4, only actively supported [PHP version](https://www.php.net/supported-versions.php) will be supported.
Removing support for EOLed PHP versions as well adding support for new PHP versions will be included in MINOR releases.
### Most notable changes
1. OpenSpout is now fully typed
2. Classes and interfaces not consumed by the user are now marked as `@internal`
3. Classes used by the user are all `final`
### Reader & Writer objects
Both readers and writers have to be naturally instantiated with `new` keyword, passing the eventual needed `Options`
class as the first argument:
```php
use OpenSpout\Reader\CSV\Reader;
use OpenSpout\Reader\CSV\Options;
$options = new Options();
$options->FIELD_DELIMITER = '|';
$options->FIELD_ENCLOSURE = '@';
$reader = new Reader($options);
```
### Cell types on writes
Cell types are now handled with separate classes:
```php
use OpenSpout\Common\Entity\Cell;
use OpenSpout\Common\Entity\Row;
$row = new Row([
new Cell\BooleanCell(true),
new Cell\DateIntervalCell(new DateInterval('P1D')),
new Cell\DateTimeCell(new DateTimeImmutable('now')),
new Cell\EmptyCell(null),
new Cell\FormulaCell('=SUM(A1:A2)'),
new Cell\NumericCell(3),
new Cell\StringCell('foo'),
]);
```
Auto-typing is still available though:
```php
use OpenSpout\Common\Entity\Cell;
use OpenSpout\Common\Entity\Row;
$cell = Cell::fromValue(true); // Instance of Cell\BooleanCell
$row = Row::fromValues([
true,
new DateInterval('P1D'),
new DateTimeImmutable('now'),
null,
'=SUM(A1:A2)',
3,
'foo',
]);
```
## Upgrading from 2.x to 3.0
OpenSpout 3.0 introduced several backwards-incompatible changes. The upgrade from OpenSpout 2.x to 3.0 must therefore
be done with caution.
This guide is meant to ease this process. This guide is meant to ease this process.
Most notable changes ### Most notable changes
--------------------
In 2.x, styles were applied per row; it was therefore impossible to apply different styles to cells in the same row. In 2.x, styles were applied per row; it was therefore impossible to apply different styles to cells in the same row.
With the 3.0 version, this is now possible: each cell can have its own style. With the 3.0 version, this is now possible: each cell can have its own style.
Spout 3.0 tries to enforce better typing. For instance, instead of using/returning generic arrays, Spout now makes use of specific `Row` and `Cell` objects that can encapsulate more data such as type, style, value. OpenSpout 3.0 tries to enforce better typing. For instance, instead of using/returning generic arrays, OpenSpout now
makes use of specific `Row` and `Cell` objects that can encapsulate more data such as type, style, value.
Finally, **_Spout 3.2 only supports PHP 7.2 and above_**, as other PHP versions are no longer supported by the community. Finally, **_OpenSpout 3.2 only supports PHP 7.2 and above_**, as other PHP versions are no longer supported by the
community.
### Reader changes
Reader changes
--------------
Creating a reader should now be done through the Reader `ReaderEntityFactory`, instead of using the `ReaderFactory`. Creating a reader should now be done through the Reader `ReaderEntityFactory`, instead of using the `ReaderFactory`.
Also, the `ReaderFactory::create($type)` method was removed and replaced by methods for each reader: Also, the `ReaderFactory::create($type)` method was removed and replaced by methods for each reader:
```php ```php
use OpenSpout\Reader\Common\Creator\ReaderEntityFactory; // namespace is no longer "OpenSpout\Reader" use OpenSpout\Reader\Common\Creator\ReaderEntityFactory; // namespace is no longer "OpenSpout\Reader"
...
$reader = ReaderEntityFactory::createXLSXReader(); // replaces ReaderFactory::create(Type::XLSX) $reader = ReaderEntityFactory::createXLSXReader(); // replaces ReaderFactory::create(Type::XLSX)
$reader = ReaderEntityFactory::createCSVReader(); // replaces ReaderFactory::create(Type::CSV) $reader = ReaderEntityFactory::createCSVReader(); // replaces ReaderFactory::create(Type::CSV)
$reader = ReaderEntityFactory::createODSReader(); // replaces ReaderFactory::create(Type::ODS) $reader = ReaderEntityFactory::createODSReader(); // replaces ReaderFactory::create(Type::ODS)
``` ```
When iterating over the spreadsheet rows, Spout now returns `Row` objects, instead of an array containing row values. Accessing the row values should now be done this way: When iterating over the spreadsheet rows, OpenSpout now returns `Row` objects, instead of an array containing row
values. Accessing the row values should now be done this way:
```php ```php
...
foreach ($reader->getSheetIterator() as $sheet) { foreach ($reader->getSheetIterator() as $sheet) {
foreach ($sheet->getRowIterator() as $row) { // $row is a "Row" object, not an array foreach ($sheet->getRowIterator() as $row) { // $row is a "Row" object, not an array
$rowAsArray = $row->toArray(); // this is the 2.x equivalent $rowAsArray = $row->toArray(); // this is the 2.x equivalent
@ -38,20 +108,22 @@ foreach ($reader->getSheetIterator() as $sheet) {
} }
``` ```
Writer changes ### Writer changes
--------------
Writer creation follows the same change as the reader. It should now be done through the Writer `WriterEntityFactory`, instead of using the `WriterFactory`. Writer creation follows the same change as the reader. It should now be done through the Writer `WriterEntityFactory`,
instead of using the `WriterFactory`.
Also, the `WriterFactory::create($type)` method was removed and replaced by methods for each writer: Also, the `WriterFactory::create($type)` method was removed and replaced by methods for each writer:
```php ```php
use OpenSpout\Writer\Common\Creator\WriterEntityFactory; // namespace is no longer "OpenSpout\Writer" use OpenSpout\Writer\Common\Creator\WriterEntityFactory; // namespace is no longer "OpenSpout\Writer"
...
$writer = WriterEntityFactory::createXLSXWriter(); // replaces WriterFactory::create(Type::XLSX) $writer = WriterEntityFactory::createXLSXWriter(); // replaces WriterFactory::create(Type::XLSX)
$writer = WriterEntityFactory::createCSVWriter(); // replaces WriterFactory::create(Type::CSV) $writer = WriterEntityFactory::createCSVWriter(); // replaces WriterFactory::create(Type::CSV)
$writer = WriterEntityFactory::createODSWriter(); // replaces WriterFactory::create(Type::ODS) $writer = WriterEntityFactory::createODSWriter(); // replaces WriterFactory::create(Type::ODS)
``` ```
Adding rows is also done differently: instead of passing an array, the writer now takes in a `Row` object (or an array of `Row`). Creating such objects can easily be done this way: Adding rows is also done differently: instead of passing an array, the writer now takes in a `Row` object (or an
array of `Row`). Creating such objects can easily be done this way:
```php ```php
// Adding a row from an array of values (2.x equivalent) // Adding a row from an array of values (2.x equivalent)
$cellValues = ['foo', 12345]; $cellValues = ['foo', 12345];
@ -65,8 +137,8 @@ $row2 = WriterEntityFactory::createRow([$cell1, $cell2]);
$writer->addRows([$row1, $row2]); $writer->addRows([$row1, $row2]);
``` ```
Namespace changes for styles ### Namespace changes for styles
-----------------
The namespaces for styles have changed. Styles are still created by using a `builder` class. The namespaces for styles have changed. Styles are still created by using a `builder` class.
For the builder, please update your import statements to use the following namespaces: For the builder, please update your import statements to use the following namespaces:
@ -83,7 +155,8 @@ If your are using these classes directly via an import statement in your code, p
OpenSpout\Common\Entity\Style\Color OpenSpout\Common\Entity\Style\Color
OpenSpout\Common\Entity\Style\Style OpenSpout\Common\Entity\Style\Style
Handling of empty rows ### Handling of empty rows
----------------------
In 2.x, empty rows were not added to the spreadsheet. In 2.x, empty rows were not added to the spreadsheet.
In 3.0, `addRow` now always writes a row to the spreadsheet: when the row does not contain any cells, an empty row is created in the sheet. In 3.0, `addRow` now always writes a row to the spreadsheet: when the row does not contain any cells, an empty row
is created in the sheet.

View File

@ -28,8 +28,9 @@
], ],
"homepage": "https://github.com/openspout/openspout", "homepage": "https://github.com/openspout/openspout",
"require": { "require": {
"php": "~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0", "php": "~8.2.0 || ~8.3.0 || ~8.4.0",
"ext-dom": "*", "ext-dom": "*",
"ext-fileinfo": "*",
"ext-filter": "*", "ext-filter": "*",
"ext-libxml": "*", "ext-libxml": "*",
"ext-xmlreader": "*", "ext-xmlreader": "*",
@ -37,14 +38,17 @@
}, },
"require-dev": { "require-dev": {
"ext-zlib": "*", "ext-zlib": "*",
"friendsofphp/php-cs-fixer": "^3.4", "friendsofphp/php-cs-fixer": "^3.65.0",
"phpstan/phpstan": "^1.4", "infection/infection": "^0.29.8",
"phpstan/phpstan-phpunit": "^1.0", "phpbench/phpbench": "^1.3.1",
"phpunit/phpunit": "^9.5" "phpstan/phpstan": "^2.0.2",
"phpstan/phpstan-phpunit": "^2.0.1",
"phpstan/phpstan-strict-rules": "^2",
"phpunit/phpunit": "^11.4.3"
}, },
"suggest": { "suggest": {
"ext-iconv": "To handle non UTF-8 CSV files (if \"php-intl\" is not already installed or is too limited)", "ext-iconv": "To handle non UTF-8 CSV files (if \"php-mbstring\" is not already installed or is too limited)",
"ext-intl": "To handle non UTF-8 CSV files (if \"iconv\" is not already installed)" "ext-mbstring": "To handle non UTF-8 CSV files (if \"iconv\" is not already installed)"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {
@ -52,13 +56,16 @@
} }
}, },
"autoload-dev": { "autoload-dev": {
"psr-4": {
"OpenSpout\\Benchmarks\\": "benchmarks/"
},
"classmap": [ "classmap": [
"tests/" "tests/"
] ]
}, },
"config": { "config": {
"platform": { "allow-plugins": {
"php": "7.3" "infection/extension-installer": true
} }
}, },
"extra": { "extra": {

View File

@ -0,0 +1,6 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"local>Slamdunk/.github:renovate-config"
]
}

Some files were not shown because too many files have changed in this diff Show More