Compare commits

...

No commits in common. "dev74" and "upstream4" have entirely different histories.

273 changed files with 7724 additions and 15469 deletions

View File

@ -1,12 +0,0 @@
# -*- coding: utf-8 mode: yaml -*- vim:sw=2:sts=2:et:ai:si:sta:fenc=utf-8
composer:
profiles: [ dev, dist ]
dev:
link: true
require:
nulib/php: ^7.4-dev
dist:
link: false
require:
nulib/php: ^0.4.0p74

View File

10
.gitignore vendored
View File

@ -1,10 +0,0 @@
.~lock*#
.*.swp
/vendor/
/.idea/shelf/
/.idea/workspace.xml
/.idea/httpRequests/
/.idea/dataSources/
/.idea/dataSources.local.xml
/.phpunit.result.cache

8
.idea/.gitignore generated vendored
View File

@ -1,8 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@ -1,12 +0,0 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="GrazieInspection" enabled="false" level="GRAMMAR_ERROR" enabled_by_default="false" />
<inspection_tool class="LanguageDetectionInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
<option name="processCode" value="true" />
<option name="processLiterals" value="true" />
<option name="processComments" value="true" />
</inspection_tool>
</profile>
</component>

17
.idea/misc.xml generated
View File

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectInspectionProfilesVisibleTreeState">
<entry key="Project Default">
<profile-state>
<expanded-state>
<State />
</expanded-state>
<selected-state>
<State>
<id>Angular</id>
</State>
</selected-state>
</profile-state>
</entry>
</component>
</project>

8
.idea/modules.xml generated
View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/nulib-spout.iml" filepath="$PROJECT_DIR$/.idea/nulib-spout.iml" />
</modules>
</component>
</project>

13
.idea/nulib-spout.iml generated
View File

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" packagePrefix="nulib\" />
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" packagePrefix="nulib\ext\" />
<sourceFolder url="file://$MODULE_DIR$/upstream-3.x/src" isTestSource="false" packagePrefix="OpenSpout\" />
<excludeFolder url="file://$MODULE_DIR$/vendor" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -1,23 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PhpDockerContainerSettings">
<list>
<map>
<entry key="125ffb9d-fd5f-4e71-8182-94191665795a">
<value>
<DockerContainerSettings>
<option name="version" value="1" />
<option name="volumeBindings">
<list>
<DockerVolumeBindingImpl>
<option name="containerPath" value="/opt/project" />
</DockerVolumeBindingImpl>
</list>
</option>
</DockerContainerSettings>
</value>
</entry>
</map>
</list>
</component>
</project>

View File

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PhpTestFrameworkVersionCache">
<tools_cache>
<tool tool_name="PHPUnit">
<cache>
<versions>
<info id="Local/vendor/autoload.php" version="9.6.21" />
</versions>
</cache>
</tool>
</tools_cache>
</component>
</project>

107
.idea/php.xml generated
View File

@ -1,107 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="MessDetectorOptionsConfiguration">
<option name="transferred" value="true" />
</component>
<component name="PHPCSFixerOptionsConfiguration">
<option name="transferred" value="true" />
</component>
<component name="PHPCodeSnifferOptionsConfiguration">
<option name="highlightLevel" value="WARNING" />
<option name="transferred" value="true" />
</component>
<component name="PhpCSFixer">
<phpcsfixer_settings>
<PhpCSFixerConfiguration tool_path="$PROJECT_DIR$/vendor/bin/php-cs-fixer" />
</phpcsfixer_settings>
</component>
<component name="PhpIncludePathManager">
<include_path>
<path value="$PROJECT_DIR$/vendor/sebastian/global-state" />
<path value="$PROJECT_DIR$/vendor/sebastian/type" />
<path value="$PROJECT_DIR$/vendor/sebastian/environment" />
<path value="$PROJECT_DIR$/vendor/sebastian/version" />
<path value="$PROJECT_DIR$/vendor/sebastian/exporter" />
<path value="$PROJECT_DIR$/vendor/sebastian/code-unit-reverse-lookup" />
<path value="$PROJECT_DIR$/vendor/sebastian/object-enumerator" />
<path value="$PROJECT_DIR$/vendor/sebastian/object-reflector" />
<path value="$PROJECT_DIR$/vendor/sebastian/comparator" />
<path value="$PROJECT_DIR$/vendor/sebastian/diff" />
<path value="$PROJECT_DIR$/vendor/symfony/string" />
<path value="$PROJECT_DIR$/vendor/symfony/process" />
<path value="$PROJECT_DIR$/vendor/sebastian/resource-operations" />
<path value="$PROJECT_DIR$/vendor/sebastian/complexity" />
<path value="$PROJECT_DIR$/vendor/sebastian/cli-parser" />
<path value="$PROJECT_DIR$/vendor/sebastian/recursion-context" />
<path value="$PROJECT_DIR$/vendor/phpunit/php-file-iterator" />
<path value="$PROJECT_DIR$/vendor/friendsofphp/php-cs-fixer" />
<path value="$PROJECT_DIR$/vendor/phpunit/php-code-coverage" />
<path value="$PROJECT_DIR$/vendor/phpunit/php-text-template" />
<path value="$PROJECT_DIR$/vendor/nikic/php-parser" />
<path value="$PROJECT_DIR$/vendor/phar-io/version" />
<path value="$PROJECT_DIR$/vendor/phpstan/phpstan-phpunit" />
<path value="$PROJECT_DIR$/vendor/phpstan/phpstan" />
<path value="$PROJECT_DIR$/vendor/psr/event-dispatcher" />
<path value="$PROJECT_DIR$/vendor/psr/container" />
<path value="$PROJECT_DIR$/vendor/composer" />
<path value="$PROJECT_DIR$/vendor/sebastian/code-unit" />
<path value="$PROJECT_DIR$/vendor/sebastian/lines-of-code" />
<path value="$PROJECT_DIR$/vendor/phpunit/php-invoker" />
<path value="$PROJECT_DIR$/vendor/phpunit/php-timer" />
<path value="$PROJECT_DIR$/vendor/psr/log" />
<path value="$PROJECT_DIR$/vendor/phpunit/phpunit" />
<path value="$PROJECT_DIR$/vendor/myclabs/deep-copy" />
<path value="$PROJECT_DIR$/vendor/doctrine/instantiator" />
<path value="$PROJECT_DIR$/vendor/phar-io/manifest" />
<path value="$PROJECT_DIR$/vendor/clue/ndjson-react" />
<path value="$PROJECT_DIR$/vendor/evenement/evenement" />
<path value="$PROJECT_DIR$/vendor/theseer/tokenizer" />
<path value="$PROJECT_DIR$/vendor/react/promise" />
<path value="$PROJECT_DIR$/vendor/react/cache" />
<path value="$PROJECT_DIR$/vendor/fidry/cpu-core-counter" />
<path value="$PROJECT_DIR$/vendor/react/dns" />
<path value="$PROJECT_DIR$/vendor/react/child-process" />
<path value="$PROJECT_DIR$/vendor/react/event-loop" />
<path value="$PROJECT_DIR$/vendor/react/socket" />
<path value="$PROJECT_DIR$/vendor/react/stream" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-intl-grapheme" />
<path value="$PROJECT_DIR$/vendor/symfony/console" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-php81" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-php73" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-intl-normalizer" />
<path value="$PROJECT_DIR$/vendor/symfony/service-contracts" />
<path value="$PROJECT_DIR$/vendor/symfony/finder" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-php80" />
<path value="$PROJECT_DIR$/vendor/symfony/deprecation-contracts" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-ctype" />
<path value="$PROJECT_DIR$/vendor/symfony/filesystem" />
<path value="$PROJECT_DIR$/vendor/symfony/event-dispatcher-contracts" />
<path value="$PROJECT_DIR$/vendor/symfony/options-resolver" />
<path value="$PROJECT_DIR$/vendor/symfony/event-dispatcher" />
<path value="$PROJECT_DIR$/vendor/symfony/stopwatch" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-mbstring" />
<path value="$PROJECT_DIR$/vendor/nulib/tests" />
<path value="$PROJECT_DIR$/vendor/nulib/php" />
<path value="$PROJECT_DIR$/vendor/symfony/yaml" />
</include_path>
</component>
<component name="PhpProjectSharedConfiguration" php_language_level="7.4">
<option name="suggestChangeDefaultLanguageLevel" value="false" />
</component>
<component name="PhpStan">
<PhpStan_settings>
<PhpStanConfiguration tool_path="$PROJECT_DIR$/vendor/bin/phpstan" />
</PhpStan_settings>
</component>
<component name="PhpStanOptionsConfiguration">
<option name="transferred" value="true" />
</component>
<component name="PhpUnit">
<phpunit_settings>
<PhpUnitSettings custom_loader_path="$PROJECT_DIR$/vendor/autoload.php" phpunit_phar_path="" />
</phpunit_settings>
</component>
<component name="PsalmOptionsConfiguration">
<option name="transferred" value="true" />
</component>
</project>

10
.idea/phpunit.xml generated
View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PHPUnit">
<option name="directories">
<list>
<option value="$PROJECT_DIR$/tests" />
</list>
</option>
</component>
</project>

6
.idea/vcs.xml generated
View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View File

@ -1,27 +1,12 @@
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
UPSTREAM=upstream3
DEVELOP=dev74
FEATURE=wip74/
RELEASE=rel74-
MAIN=dist74
UPSTREAM=upstream4
DEVELOP=dev82
FEATURE=wip82/
RELEASE=rel82-
MAIN=dist82
TAG_PREFIX=
TAG_SUFFIX=p74
HOTFIX=hotf74-
TAG_SUFFIX=p82
HOTFIX=hotf82-
DIST=
NOAUTO=
AFTER_CREATE_RELEASE='
set -x
pman --composer-select-profile dist
composer u || exit 1
git commit -am "<pman>deps de dist"
true
'
AFTER_MERGE_RELEASE='
set -x
pman --composer-select-profile dev
composer u || exit 1
git commit -am "<pman>deps de dev"
true
'
NOAUTO=1

View File

@ -1,8 +0,0 @@
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
# Chemin vers runphp, e.g sbin/runphp
RUNPHP=
# Si RUNPHP n'est pas défini, les variables suivantes peuvent être définies
DIST=d11
#REGISTRY=pubdocker.univ-reunion.fr/dist

View File

@ -1,17 +0,0 @@
## Release 0.4.0p82 du 14/03/2025-15:40
## Release 0.4.0p74 du 14/03/2025-15:35
* `7e90311` tracer le hook
* `ddce350` ajout config .pman.yml
## Release 0.3.1p82 du 04/03/2025-12:17
* `519f341` application patch page_order
* `85e9740` réorganiser les patches
## Release 0.3.0p82 du 01/03/2025-09:28
## Release 0.3.0p74 du 01/03/2025-09:23
release initiale

View File

@ -1 +0,0 @@
0.4.0

View File

@ -1,62 +0,0 @@
{
"name": "nulib/spout",
"type": "library",
"description": "wrapper pour openspout/openspout",
"repositories": [
{
"type": "path",
"url": "../nulib"
},
{
"type": "composer",
"url": "https://repos.univ-reunion.fr/composer"
}
],
"extra": {
"branch-alias": {
"dev-dev74": "7.4.x-dev",
"dev-dev82": "8.2.x-dev"
}
},
"replace": {
"openspout/openspout": "v3.7.4"
},
"require": {
"nulib/php": "^7.4-dev",
"ext-dom": "*",
"ext-filter": "*",
"ext-libxml": "*",
"ext-xmlreader": "*",
"ext-zip": "*",
"php": "^7.4"
},
"require-dev": {
"nulib/tests": "^7.4",
"friendsofphp/php-cs-fixer": "^3.4",
"phpstan/phpstan": "^1.4",
"phpstan/phpstan-phpunit": "^1.0",
"ext-zlib": "*"
},
"autoload": {
"psr-4": {
"nulib\\": "src",
"OpenSpout\\": "openspout3/src"
}
},
"autoload-dev": {
"psr-4": {
"nulib\\ext\\": "tests"
}
},
"authors": [
{
"name": "Jephte Clain",
"email": "Jephte.Clain@univ-reunion.fr"
}
],
"config": {
"allow-plugins": {
"infection/extension-installer": false
}
}
}

4590
composer.lock generated

File diff suppressed because it is too large Load Diff

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,319 +0,0 @@
<?php
namespace OpenSpout\Reader\XLSX\Helper;
use OpenSpout\Reader\Exception\InvalidValueException;
use OpenSpout\Reader\XLSX\Manager\SharedStringsManager;
use OpenSpout\Reader\XLSX\Manager\StyleManager;
/**
* This class provides helper functions to format cell values.
*/
class CellValueFormatter
{
/** Definition of all possible cell types */
public const CELL_TYPE_INLINE_STRING = 'inlineStr';
public const CELL_TYPE_STR = 'str';
public const CELL_TYPE_SHARED_STRING = 's';
public const CELL_TYPE_BOOLEAN = 'b';
public const CELL_TYPE_NUMERIC = 'n';
public const CELL_TYPE_DATE = 'd';
public const CELL_TYPE_ERROR = 'e';
/** Definition of XML nodes names used to parse data */
public const XML_NODE_VALUE = 'v';
public const XML_NODE_INLINE_STRING_VALUE = 't';
/** Definition of XML attributes used to parse data */
public const XML_ATTRIBUTE_TYPE = 't';
public const XML_ATTRIBUTE_STYLE_ID = 's';
/** Constants used for date formatting */
public const NUM_SECONDS_IN_ONE_DAY = 86400;
/** @var SharedStringsManager Manages shared strings */
protected $sharedStringsManager;
/** @var StyleManager Manages styles */
protected $styleManager;
/** @var bool Whether date/time values should be returned as PHP objects or be formatted as strings */
protected $shouldFormatDates;
/** @var bool Whether date/time values should use a calendar starting in 1904 instead of 1900 */
protected $shouldUse1904Dates;
/** @var \OpenSpout\Common\Helper\Escaper\XLSX Used to unescape XML data */
protected $escaper;
/**
* @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
* @param \OpenSpout\Common\Helper\Escaper\XLSX $escaper Used to unescape XML data
*/
public function __construct($sharedStringsManager, $styleManager, $shouldFormatDates, $shouldUse1904Dates, $escaper)
{
$this->sharedStringsManager = $sharedStringsManager;
$this->styleManager = $styleManager;
$this->shouldFormatDates = $shouldFormatDates;
$this->shouldUse1904Dates = $shouldUse1904Dates;
$this->escaper = $escaper;
}
/**
* Returns the (unescaped) correctly marshalled, cell value associated to the given XML node.
*
* @param \DOMElement $node
*
* @throws InvalidValueException If the value is not valid
*
* @return bool|\DateTime|float|int|string The value associated with the cell
*/
public function extractAndFormatNodeValue($node)
{
// Default cell type is "n"
$cellType = $node->getAttribute(self::XML_ATTRIBUTE_TYPE) ?: self::CELL_TYPE_NUMERIC;
$cellStyleId = (int) $node->getAttribute(self::XML_ATTRIBUTE_STYLE_ID);
$vNodeValue = $this->getVNodeValue($node);
if (('' === $vNodeValue) && (self::CELL_TYPE_INLINE_STRING !== $cellType)) {
return $vNodeValue;
}
switch ($cellType) {
case self::CELL_TYPE_INLINE_STRING:
return $this->formatInlineStringCellValue($node);
case self::CELL_TYPE_SHARED_STRING:
return $this->formatSharedStringCellValue($vNodeValue);
case self::CELL_TYPE_STR:
return $this->formatStrCellValue($vNodeValue);
case self::CELL_TYPE_BOOLEAN:
return $this->formatBooleanCellValue($vNodeValue);
case self::CELL_TYPE_NUMERIC:
return $this->formatNumericCellValue($vNodeValue, $cellStyleId);
case self::CELL_TYPE_DATE:
return $this->formatDateCellValue($vNodeValue);
default:
throw new InvalidValueException($vNodeValue);
}
}
/**
* Returns the cell's string value from a node's nested value node.
*
* @param \DOMElement $node
*
* @return string The value associated with the cell
*/
protected function getVNodeValue($node)
{
// for cell types having a "v" tag containing the value.
// if not, the returned value should be empty string.
$vNode = $node->getElementsByTagName(self::XML_NODE_VALUE)->item(0);
return (null !== $vNode) ? $vNode->nodeValue : '';
}
/**
* Returns the cell String value where string is inline.
*
* @param \DOMElement $node
*
* @return string The value associated with the cell
*/
protected function formatInlineStringCellValue($node)
{
// inline strings are formatted this way (they can contain any number of <t> nodes):
// <c r="A1" t="inlineStr"><is><t>[INLINE_STRING]</t><t>[INLINE_STRING_2]</t></is></c>
$tNodes = $node->getElementsByTagName(self::XML_NODE_INLINE_STRING_VALUE);
$cellValue = '';
for ($i = 0; $i < $tNodes->count(); ++$i) {
$tNode = $tNodes->item($i);
$cellValue .= $this->escaper->unescape($tNode->nodeValue);
}
return $cellValue;
}
/**
* Returns the cell String value from shared-strings file using nodeValue index.
*
* @param string $nodeValue
*
* @return string The value associated with the cell
*/
protected function formatSharedStringCellValue($nodeValue)
{
// shared strings are formatted this way:
// <c r="A1" t="s"><v>[SHARED_STRING_INDEX]</v></c>
$sharedStringIndex = (int) $nodeValue;
$escapedCellValue = $this->sharedStringsManager->getStringAtIndex($sharedStringIndex);
return $this->escaper->unescape($escapedCellValue);
}
/**
* Returns the cell String value, where string is stored in value node.
*
* @param string $nodeValue
*
* @return string The value associated with the cell
*/
protected function formatStrCellValue($nodeValue)
{
$escapedCellValue = trim($nodeValue);
return $this->escaper->unescape($escapedCellValue);
}
/**
* Returns the cell Numeric value from string of nodeValue.
* The value can also represent a timestamp and a DateTime will be returned.
*
* @param string $nodeValue
* @param int $cellStyleId 0 being the default style
*
* @return \DateTime|float|int The value associated with the cell
*/
protected function formatNumericCellValue($nodeValue, $cellStyleId)
{
// Numeric values can represent numbers as well as timestamps.
// We need to look at the style of the cell to determine whether it is one or the other.
$shouldFormatAsDate = $this->styleManager->shouldFormatNumericValueAsDate($cellStyleId);
if ($shouldFormatAsDate) {
$cellValue = $this->formatExcelTimestampValue((float) $nodeValue, $cellStyleId);
} else {
$nodeIntValue = (int) $nodeValue;
$nodeFloatValue = (float) $nodeValue;
$cellValue = ((float) $nodeIntValue === $nodeFloatValue) ? $nodeIntValue : $nodeFloatValue;
}
return $cellValue;
}
/**
* Returns a cell's PHP Date value, associated to the given timestamp.
* NOTE: The timestamp is a float representing the number of days since the base Excel date:
* Dec 30th 1899, 1900 or Jan 1st, 1904, depending on the Workbook setting.
* NOTE: The timestamp can also represent a time, if it is a value between 0 and 1.
*
* @see ECMA-376 Part 1 - §18.17.4
*
* @param float $nodeValue
* @param int $cellStyleId 0 being the default style
*
* @throws InvalidValueException If the value is not a valid timestamp
*
* @return \DateTime The value associated with the cell
*/
protected function formatExcelTimestampValue($nodeValue, $cellStyleId)
{
if ($this->isValidTimestampValue($nodeValue)) {
$cellValue = $this->formatExcelTimestampValueAsDateTimeValue($nodeValue, $cellStyleId);
} else {
throw new InvalidValueException($nodeValue);
}
return $cellValue;
}
/**
* Returns whether the given timestamp is supported by SpreadsheetML.
*
* @see ECMA-376 Part 1 - §18.17.4 - this specifies the timestamp boundaries.
*
* @param float $timestampValue
*
* @return bool
*/
protected function isValidTimestampValue($timestampValue)
{
// @NOTE: some versions of Excel don't support negative dates (e.g. Excel for Mac 2011)
return
$this->shouldUse1904Dates && $timestampValue >= -695055 && $timestampValue <= 2957003.9999884
|| !$this->shouldUse1904Dates && $timestampValue >= -693593 && $timestampValue <= 2958465.9999884
;
}
/**
* Returns a cell's PHP DateTime value, associated to the given timestamp.
* Only the time value matters. The date part is set to the base Excel date:
* Dec 30th 1899, 1900 or Jan 1st, 1904, depending on the Workbook setting.
*
* @param float $nodeValue
* @param int $cellStyleId 0 being the default style
*
* @return \DateTime|string The value associated with the cell
*/
protected function formatExcelTimestampValueAsDateTimeValue($nodeValue, $cellStyleId)
{
$baseDate = $this->shouldUse1904Dates ? '1904-01-01' : '1899-12-30';
$daysSinceBaseDate = (int) $nodeValue;
$timeRemainder = fmod($nodeValue, 1);
$secondsRemainder = round($timeRemainder * self::NUM_SECONDS_IN_ONE_DAY, 0);
$dateObj = \DateTime::createFromFormat('|Y-m-d', $baseDate);
$dateObj->modify('+'.$daysSinceBaseDate.'days');
$dateObj->modify('+'.$secondsRemainder.'seconds');
if ($this->shouldFormatDates) {
//$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;
}
return $cellValue;
}
/**
* Returns the cell Boolean value from a specific node's Value.
*
* @param string $nodeValue
*
* @return bool The value associated with the cell
*/
protected function formatBooleanCellValue($nodeValue)
{
return (bool) $nodeValue;
}
/**
* Returns a cell's PHP Date value, associated to the given stored nodeValue.
*
* @see ECMA-376 Part 1 - §18.17.4
*
* @param string $nodeValue ISO 8601 Date string
*
* @throws InvalidValueException If the value is not a valid date
*
* @return \DateTime|string The value associated with the cell
*/
protected function formatDateCellValue($nodeValue)
{
// Mitigate thrown Exception on invalid date-time format (http://php.net/manual/en/datetime.construct.php)
try {
$cellValue = ($this->shouldFormatDates) ? $nodeValue : new \DateTime($nodeValue);
} catch (\Exception $e) {
throw new InvalidValueException($nodeValue);
}
return $cellValue;
}
}

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();
}

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