MDL-70302 core: Upgrade spout to 3.1.0

This commit is contained in:
Peter Dias 2021-01-13 10:50:45 +08:00
parent fee3970787
commit 23df19225a
68 changed files with 787 additions and 311 deletions

View file

@ -23,7 +23,7 @@ class Psr4Autoloader
*/
public function register()
{
spl_autoload_register([$this, 'loadClass']);
\spl_autoload_register([$this, 'loadClass']);
}
/**
@ -40,10 +40,10 @@ class Psr4Autoloader
public function addNamespace($prefix, $baseDir, $prepend = false)
{
// normalize namespace prefix
$prefix = trim($prefix, '\\') . '\\';
$prefix = \trim($prefix, '\\') . '\\';
// normalize the base directory with a trailing separator
$baseDir = rtrim($baseDir, DIRECTORY_SEPARATOR) . '/';
$baseDir = \rtrim($baseDir, DIRECTORY_SEPARATOR) . '/';
// initialize the namespace prefix array
if (isset($this->prefixes[$prefix]) === false) {
@ -52,9 +52,9 @@ class Psr4Autoloader
// retain the base directory for the namespace prefix
if ($prepend) {
array_unshift($this->prefixes[$prefix], $baseDir);
\array_unshift($this->prefixes[$prefix], $baseDir);
} else {
array_push($this->prefixes[$prefix], $baseDir);
\array_push($this->prefixes[$prefix], $baseDir);
}
}
@ -72,12 +72,12 @@ class Psr4Autoloader
// work backwards through the namespace names of the fully-qualified
// class name to find a mapped file name
while (($pos = strrpos($prefix, '\\')) !== false) {
while (($pos = \strrpos($prefix, '\\')) !== false) {
// retain the trailing namespace separator in the prefix
$prefix = substr($class, 0, $pos + 1);
$prefix = \substr($class, 0, $pos + 1);
// the rest is the relative class name
$relativeClass = substr($class, $pos + 1);
$relativeClass = \substr($class, $pos + 1);
// try to load a mapped file for the prefix and relative class
$mappedFile = $this->loadMappedFile($prefix, $relativeClass);
@ -87,7 +87,7 @@ class Psr4Autoloader
// remove the trailing namespace separator for the next iteration
// of strrpos()
$prefix = rtrim($prefix, '\\');
$prefix = \rtrim($prefix, '\\');
}
// never found a mapped file
@ -115,7 +115,7 @@ class Psr4Autoloader
// replace namespace separators with directory separators
// in the relative class name, append with .php
$file = $baseDir
. str_replace('\\', '/', $relativeClass)
. \str_replace('\\', '/', $relativeClass)
. '.php';
// if the mapped file exists, require it
@ -137,7 +137,7 @@ class Psr4Autoloader
*/
protected function requireFile($file)
{
if (file_exists($file)) {
if (\file_exists($file)) {
require $file;
return true;

View file

@ -8,7 +8,7 @@ require_once 'Psr4Autoloader.php';
* @var string
* Full path to "src/Spout" which is what we want "Box\Spout" to map to.
*/
$srcBaseDirectory = dirname(dirname(__FILE__));
$srcBaseDirectory = \dirname(\dirname(__FILE__));
$loader = new Psr4Autoloader();
$loader->register();

View file

@ -91,6 +91,14 @@ class Cell
return !$this->isError() ? $this->value : null;
}
/**
* @return mixed
*/
public function getValueEvenIfError()
{
return $this->value;
}
/**
* @param Style|null $style
*/

View file

@ -70,7 +70,7 @@ class Row
*/
public function getCellAtIndex($cellIndex)
{
return isset($this->cells[$cellIndex]) ? $this->cells[$cellIndex] : null;
return $this->cells[$cellIndex] ?? null;
}
/**
@ -89,7 +89,13 @@ class Row
*/
public function getNumCells()
{
return count($this->cells);
// 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;
}
/**
@ -116,7 +122,7 @@ class Row
*/
public function toArray()
{
return array_map(function (Cell $cell) {
return \array_map(function (Cell $cell) {
return $cell->getValue();
}, $this->cells);
}

View file

@ -93,7 +93,7 @@ class BorderPart
*/
public function setName($name)
{
if (!in_array($name, self::$allowedNames)) {
if (!\in_array($name, self::$allowedNames)) {
throw new InvalidNameException($name);
}
$this->name = $name;
@ -114,7 +114,7 @@ class BorderPart
*/
public function setStyle($style)
{
if (!in_array($style, self::$allowedStyles)) {
if (!\in_array($style, self::$allowedStyles)) {
throw new InvalidStyleException($style);
}
$this->style = $style;
@ -152,7 +152,7 @@ class BorderPart
*/
public function setWidth($width)
{
if (!in_array($width, self::$allowedWidths)) {
if (!\in_array($width, self::$allowedWidths)) {
throw new InvalidWidthException($width);
}
$this->width = $width;

View file

@ -0,0 +1,32 @@
<?php
namespace Box\Spout\Common\Entity\Style;
/**
* Class Alignment
* This class provides constants to work with text alignment.
*/
abstract class CellAlignment
{
const LEFT = 'left';
const RIGHT = 'right';
const CENTER = 'center';
const JUSTIFY = 'justify';
private static $VALID_ALIGNMENTS = [
self::LEFT => 1,
self::RIGHT => 1,
self::CENTER => 1,
self::JUSTIFY => 1,
];
/**
* @param string $cellAlignment
*
* @return bool Whether the given cell alignment is valid
*/
public static function isValid($cellAlignment)
{
return isset(self::$VALID_ALIGNMENTS[$cellAlignment]);
}
}

View file

@ -8,7 +8,7 @@ use Box\Spout\Common\Exception\InvalidColorException;
* Class Color
* This class provides constants and functions to work with colors
*/
class Color
abstract class Color
{
/** Standard colors - based on Office Online */
const BLACK = '000000';
@ -38,7 +38,7 @@ class Color
self::throwIfInvalidColorComponentValue($green);
self::throwIfInvalidColorComponentValue($blue);
return strtoupper(
return \strtoupper(
self::convertColorComponentToHex($red) .
self::convertColorComponentToHex($green) .
self::convertColorComponentToHex($blue)
@ -54,7 +54,7 @@ class Color
*/
protected static function throwIfInvalidColorComponentValue($colorComponent)
{
if (!is_int($colorComponent) || $colorComponent < 0 || $colorComponent > 255) {
if (!\is_int($colorComponent) || $colorComponent < 0 || $colorComponent > 255) {
throw new InvalidColorException("The RGB components must be between 0 and 255. Received: $colorComponent");
}
}
@ -67,7 +67,7 @@ class Color
*/
protected static function convertColorComponentToHex($colorComponent)
{
return str_pad(dechex($colorComponent), 2, '0', STR_PAD_LEFT);
return \str_pad(\dechex($colorComponent), 2, '0', STR_PAD_LEFT);
}
/**

View file

@ -8,7 +8,7 @@ namespace Box\Spout\Common\Entity\Style;
*/
class Style
{
/** Default font values */
/** Default values */
const DEFAULT_FONT_SIZE = 11;
const DEFAULT_FONT_COLOR = Color::BLACK;
const DEFAULT_FONT_NAME = 'Arial';
@ -54,6 +54,13 @@ class Style
/** @var bool Whether specific font properties should be applied */
private $shouldApplyFont = false;
/** @var bool Whether specific cell alignment should be applied */
private $shouldApplyCellAlignment = false;
/** @var string Cell alignment */
private $cellAlignment;
/** @var bool Whether the cell alignment property was set */
private $hasSetCellAlignment = false;
/** @var bool Whether the text should wrap in the cell (useful for long or multi-lines text) */
private $shouldWrapText = false;
/** @var bool Whether the wrap text property was set */
@ -71,6 +78,12 @@ class Style
/** @var bool */
private $hasSetBackgroundColor = false;
/** @var string Format */
private $format;
/** @var bool */
private $hasSetFormat = false;
/**
* @return int|null
*/
@ -319,6 +332,44 @@ class Style
return $this->hasSetFontName;
}
/**
* @return string
*/
public function getCellAlignment()
{
return $this->cellAlignment;
}
/**
* @param string $cellAlignment The cell alignment
*
* @return Style
*/
public function setCellAlignment($cellAlignment)
{
$this->cellAlignment = $cellAlignment;
$this->hasSetCellAlignment = true;
$this->shouldApplyCellAlignment = true;
return $this;
}
/**
* @return bool
*/
public function hasSetCellAlignment()
{
return $this->hasSetCellAlignment;
}
/**
* @return bool Whether specific cell alignment should be applied
*/
public function shouldApplyCellAlignment()
{
return $this->shouldApplyCellAlignment;
}
/**
* @return bool
*/
@ -383,4 +434,33 @@ class Style
{
return $this->hasSetBackgroundColor;
}
/**
* Sets format
* @param string $format
* @return Style
*/
public function setFormat($format)
{
$this->hasSetFormat = true;
$this->format = $format;
return $this;
}
/**
* @return string
*/
public function getFormat()
{
return $this->format;
}
/**
* @return bool Whether format should be applied
*/
public function shouldApplyFormat()
{
return $this->hasSetFormat;
}
}

View file

@ -23,7 +23,7 @@ class CellTypeHelper
*/
public static function isNonEmptyString($value)
{
return (gettype($value) === 'string' && $value !== '');
return (\gettype($value) === 'string' && $value !== '');
}
/**
@ -35,7 +35,7 @@ class CellTypeHelper
*/
public static function isNumeric($value)
{
$valueType = gettype($value);
$valueType = \gettype($value);
return ($valueType === 'integer' || $valueType === 'double');
}
@ -49,7 +49,7 @@ class CellTypeHelper
*/
public static function isBoolean($value)
{
return gettype($value) === 'boolean';
return \gettype($value) === 'boolean';
}
/**

View file

@ -61,7 +61,7 @@ class EncodingHelper
$bomUsed = $this->supportedEncodingsWithBom[$encoding];
// we skip the N first bytes
$byteOffsetToSkipBom = strlen($bomUsed);
$byteOffsetToSkipBom = \strlen($bomUsed);
}
return $byteOffsetToSkipBom;
@ -80,9 +80,9 @@ class EncodingHelper
$this->globalFunctionsHelper->rewind($filePointer);
if (array_key_exists($encoding, $this->supportedEncodingsWithBom)) {
if (\array_key_exists($encoding, $this->supportedEncodingsWithBom)) {
$potentialBom = $this->supportedEncodingsWithBom[$encoding];
$numBytesInBom = strlen($potentialBom);
$numBytesInBom = \strlen($potentialBom);
$hasBOM = ($this->globalFunctionsHelper->fgets($filePointer, $numBytesInBom + 1) === $potentialBom);
}

View file

@ -18,14 +18,14 @@ class ODS implements EscaperInterface
{
// @NOTE: Using ENT_QUOTES as XML entities ('<', '>', '&') as well as
// single/double quotes (for XML attributes) need to be encoded.
if (defined('ENT_DISALLOWED')) {
if (\defined('ENT_DISALLOWED')) {
// 'ENT_DISALLOWED' ensures that invalid characters in the given document type are replaced.
// Otherwise control characters like a vertical tab "\v" will make the XML document unreadable by the XML processor
// @link https://github.com/box/spout/issues/329
$replacedString = htmlspecialchars($string, ENT_QUOTES | ENT_DISALLOWED, 'UTF-8');
$replacedString = \htmlspecialchars($string, ENT_QUOTES | ENT_DISALLOWED, 'UTF-8');
} else {
// We are on hhvm or any other engine that does not support ENT_DISALLOWED.
$escapedString = htmlspecialchars($string, ENT_QUOTES, 'UTF-8');
$escapedString = \htmlspecialchars($string, ENT_QUOTES, 'UTF-8');
// control characters values are from 0 to 1F (hex values) in the ASCII table
// some characters should not be escaped though: "\t", "\r" and "\n".
@ -34,7 +34,7 @@ class ODS implements EscaperInterface
'\x0B-\x0C' .
// skipping "\r" (0xD)
'\x0E-\x1F]';
$replacedString = preg_replace("/$regexPattern/", '<27>', $escapedString);
$replacedString = \preg_replace("/$regexPattern/", '<27>', $escapedString);
}
return $replacedString;

View file

@ -28,7 +28,7 @@ class XLSX implements EscaperInterface
if (!$this->isAlreadyInitialized) {
$this->escapableControlCharactersPattern = $this->getEscapableControlCharactersPattern();
$this->controlCharactersEscapingMap = $this->getControlCharactersEscapingMap();
$this->controlCharactersEscapingReverseMap = array_flip($this->controlCharactersEscapingMap);
$this->controlCharactersEscapingReverseMap = \array_flip($this->controlCharactersEscapingMap);
$this->isAlreadyInitialized = true;
}
@ -47,7 +47,7 @@ class XLSX implements EscaperInterface
$escapedString = $this->escapeControlCharacters($string);
// @NOTE: Using ENT_QUOTES as XML entities ('<', '>', '&') as well as
// single/double quotes (for XML attributes) need to be encoded.
$escapedString = htmlspecialchars($escapedString, ENT_QUOTES, 'UTF-8');
$escapedString = \htmlspecialchars($escapedString, ENT_QUOTES, 'UTF-8');
return $escapedString;
}
@ -103,10 +103,10 @@ class XLSX implements EscaperInterface
// control characters values are from 0 to 1F (hex values) in the ASCII table
for ($charValue = 0x00; $charValue <= 0x1F; $charValue++) {
$character = chr($charValue);
if (preg_match("/{$this->escapableControlCharactersPattern}/", $character)) {
$charHexValue = dechex($charValue);
$escapedChar = '_x' . sprintf('%04s', strtoupper($charHexValue)) . '_';
$character = \chr($charValue);
if (\preg_match("/{$this->escapableControlCharactersPattern}/", $character)) {
$charHexValue = \dechex($charValue);
$escapedChar = '_x' . \sprintf('%04s', \strtoupper($charHexValue)) . '_';
$controlCharactersEscapingMap[$escapedChar] = $character;
}
}
@ -132,11 +132,11 @@ class XLSX implements EscaperInterface
$escapedString = $this->escapeEscapeCharacter($string);
// if no control characters
if (!preg_match("/{$this->escapableControlCharactersPattern}/", $escapedString)) {
if (!\preg_match("/{$this->escapableControlCharactersPattern}/", $escapedString)) {
return $escapedString;
}
return preg_replace_callback("/({$this->escapableControlCharactersPattern})/", function ($matches) {
return \preg_replace_callback("/({$this->escapableControlCharactersPattern})/", function ($matches) {
return $this->controlCharactersEscapingReverseMap[$matches[0]];
}, $escapedString);
}
@ -149,7 +149,7 @@ class XLSX implements EscaperInterface
*/
protected function escapeEscapeCharacter($string)
{
return preg_replace('/_(x[\dA-F]{4})_/', '_x005F_$1_', $string);
return \preg_replace('/_(x[\dA-F]{4})_/', '_x005F_$1_', $string);
}
/**
@ -171,7 +171,7 @@ class XLSX implements EscaperInterface
foreach ($this->controlCharactersEscapingMap as $escapedCharValue => $charValue) {
// only unescape characters that don't contain the escaped escape character for now
$unescapedString = preg_replace("/(?<!_x005F)($escapedCharValue)/", $charValue, $unescapedString);
$unescapedString = \preg_replace("/(?<!_x005F)($escapedCharValue)/", $charValue, $unescapedString);
}
return $this->unescapeEscapeCharacter($unescapedString);
@ -185,6 +185,6 @@ class XLSX implements EscaperInterface
*/
protected function unescapeEscapeCharacter($string)
{
return preg_replace('/_x005F(_x[\dA-F]{4}_)/', '$1', $string);
return \preg_replace('/_x005F(_x[\dA-F]{4}_)/', '$1', $string);
}
}

View file

@ -19,7 +19,7 @@ class FileSystemHelper implements FileSystemHelperInterface
*/
public function __construct($baseFolderPath)
{
$this->baseFolderRealPath = realpath($baseFolderPath);
$this->baseFolderRealPath = \realpath($baseFolderPath);
}
/**
@ -36,7 +36,7 @@ class FileSystemHelper implements FileSystemHelperInterface
$folderPath = $parentFolderPath . '/' . $folderName;
$wasCreationSuccessful = mkdir($folderPath, 0777, true);
$wasCreationSuccessful = \mkdir($folderPath, 0777, true);
if (!$wasCreationSuccessful) {
throw new IOException("Unable to create folder: $folderPath");
}
@ -60,7 +60,7 @@ class FileSystemHelper implements FileSystemHelperInterface
$filePath = $parentFolderPath . '/' . $fileName;
$wasCreationSuccessful = file_put_contents($filePath, $fileContents);
$wasCreationSuccessful = \file_put_contents($filePath, $fileContents);
if ($wasCreationSuccessful === false) {
throw new IOException("Unable to create file: $filePath");
}
@ -79,8 +79,8 @@ class FileSystemHelper implements FileSystemHelperInterface
{
$this->throwIfOperationNotInBaseFolder($filePath);
if (file_exists($filePath) && is_file($filePath)) {
unlink($filePath);
if (\file_exists($filePath) && \is_file($filePath)) {
\unlink($filePath);
}
}
@ -102,13 +102,13 @@ class FileSystemHelper implements FileSystemHelperInterface
foreach ($itemIterator as $item) {
if ($item->isDir()) {
rmdir($item->getPathname());
\rmdir($item->getPathname());
} else {
unlink($item->getPathname());
\unlink($item->getPathname());
}
}
rmdir($folderPath);
\rmdir($folderPath);
}
/**
@ -122,8 +122,8 @@ class FileSystemHelper implements FileSystemHelperInterface
*/
protected function throwIfOperationNotInBaseFolder($operationFolderPath)
{
$operationFolderRealPath = realpath($operationFolderPath);
$isInBaseFolder = (strpos($operationFolderRealPath, $this->baseFolderRealPath) === 0);
$operationFolderRealPath = \realpath($operationFolderPath);
$isInBaseFolder = (\strpos($operationFolderRealPath, $this->baseFolderRealPath) === 0);
if (!$isInBaseFolder) {
throw new IOException("Cannot perform I/O operation outside of the base folder: {$this->baseFolderRealPath}");
}

View file

@ -20,7 +20,7 @@ class GlobalFunctionsHelper
*/
public function fopen($fileName, $mode)
{
return fopen($fileName, $mode);
return \fopen($fileName, $mode);
}
/**
@ -33,7 +33,7 @@ class GlobalFunctionsHelper
*/
public function fgets($handle, $length = null)
{
return fgets($handle, $length);
return \fgets($handle, $length);
}
/**
@ -46,7 +46,7 @@ class GlobalFunctionsHelper
*/
public function fputs($handle, $string)
{
return fputs($handle, $string);
return \fputs($handle, $string);
}
/**
@ -58,7 +58,7 @@ class GlobalFunctionsHelper
*/
public function fflush($handle)
{
return fflush($handle);
return \fflush($handle);
}
/**
@ -71,7 +71,7 @@ class GlobalFunctionsHelper
*/
public function fseek($handle, $offset)
{
return fseek($handle, $offset);
return \fseek($handle, $offset);
}
/**
@ -90,9 +90,9 @@ class GlobalFunctionsHelper
// 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 = "\0";
$escapeCharacter = PHP_VERSION_ID >= 70400 ? '' : "\0";
return fgetcsv($handle, $length, $delimiter, $enclosure, $escapeCharacter);
return \fgetcsv($handle, $length, $delimiter, $enclosure, $escapeCharacter);
}
/**
@ -111,9 +111,9 @@ class GlobalFunctionsHelper
// 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 = "\0";
$escapeCharacter = PHP_VERSION_ID >= 70400 ? '' : "\0";
return fputcsv($handle, $fields, $delimiter, $enclosure, $escapeCharacter);
return \fputcsv($handle, $fields, $delimiter, $enclosure, $escapeCharacter);
}
/**
@ -126,7 +126,7 @@ class GlobalFunctionsHelper
*/
public function fwrite($handle, $string)
{
return fwrite($handle, $string);
return \fwrite($handle, $string);
}
/**
@ -138,7 +138,7 @@ class GlobalFunctionsHelper
*/
public function fclose($handle)
{
return fclose($handle);
return \fclose($handle);
}
/**
@ -150,7 +150,7 @@ class GlobalFunctionsHelper
*/
public function rewind($handle)
{
return rewind($handle);
return \rewind($handle);
}
/**
@ -162,7 +162,7 @@ class GlobalFunctionsHelper
*/
public function file_exists($fileName)
{
return file_exists($fileName);
return \file_exists($fileName);
}
/**
@ -176,7 +176,7 @@ class GlobalFunctionsHelper
{
$realFilePath = $this->convertToUseRealPath($filePath);
return file_get_contents($realFilePath);
return \file_get_contents($realFilePath);
}
/**
@ -191,13 +191,13 @@ class GlobalFunctionsHelper
$realFilePath = $filePath;
if ($this->isZipStream($filePath)) {
if (preg_match('/zip:\/\/(.*)#(.*)/', $filePath, $matches)) {
if (\preg_match('/zip:\/\/(.*)#(.*)/', $filePath, $matches)) {
$documentPath = $matches[1];
$documentInsideZipPath = $matches[2];
$realFilePath = 'zip://' . realpath($documentPath) . '#' . $documentInsideZipPath;
$realFilePath = 'zip://' . \realpath($documentPath) . '#' . $documentInsideZipPath;
}
} else {
$realFilePath = realpath($filePath);
$realFilePath = \realpath($filePath);
}
return $realFilePath;
@ -211,7 +211,7 @@ class GlobalFunctionsHelper
*/
protected function isZipStream($path)
{
return (strpos($path, 'zip://') === 0);
return (\strpos($path, 'zip://') === 0);
}
/**
@ -223,7 +223,7 @@ class GlobalFunctionsHelper
*/
public function feof($handle)
{
return feof($handle);
return \feof($handle);
}
/**
@ -235,7 +235,7 @@ class GlobalFunctionsHelper
*/
public function is_readable($fileName)
{
return is_readable($fileName);
return \is_readable($fileName);
}
/**
@ -248,7 +248,7 @@ class GlobalFunctionsHelper
*/
public function basename($path, $suffix = null)
{
return basename($path, $suffix);
return \basename($path, $suffix);
}
/**
@ -260,7 +260,7 @@ class GlobalFunctionsHelper
*/
public function header($string)
{
header($string);
\header($string);
}
/**
@ -271,8 +271,8 @@ class GlobalFunctionsHelper
*/
public function ob_end_clean()
{
if (ob_get_length() > 0) {
ob_end_clean();
if (\ob_get_length() > 0) {
\ob_end_clean();
}
}
@ -287,7 +287,7 @@ class GlobalFunctionsHelper
*/
public function iconv($string, $sourceEncoding, $targetEncoding)
{
return iconv($sourceEncoding, $targetEncoding, $string);
return \iconv($sourceEncoding, $targetEncoding, $string);
}
/**
@ -301,7 +301,7 @@ class GlobalFunctionsHelper
*/
public function mb_convert_encoding($string, $sourceEncoding, $targetEncoding)
{
return mb_convert_encoding($string, $targetEncoding, $sourceEncoding);
return \mb_convert_encoding($string, $targetEncoding, $sourceEncoding);
}
/**
@ -312,7 +312,7 @@ class GlobalFunctionsHelper
*/
public function stream_get_wrappers()
{
return stream_get_wrappers();
return \stream_get_wrappers();
}
/**
@ -324,6 +324,6 @@ class GlobalFunctionsHelper
*/
public function function_exists($functionName)
{
return function_exists($functionName);
return \function_exists($functionName);
}
}

View file

@ -18,7 +18,7 @@ class StringHelper
*/
public function __construct()
{
$this->hasMbstringSupport = extension_loaded('mbstring');
$this->hasMbstringSupport = \extension_loaded('mbstring');
}
/**
@ -32,7 +32,7 @@ class StringHelper
*/
public function getStringLength($string)
{
return $this->hasMbstringSupport ? mb_strlen($string) : strlen($string);
return $this->hasMbstringSupport ? \mb_strlen($string) : \strlen($string);
}
/**
@ -47,7 +47,7 @@ class StringHelper
*/
public function getCharFirstOccurrencePosition($char, $string)
{
$position = $this->hasMbstringSupport ? mb_strpos($string, $char) : strpos($string, $char);
$position = $this->hasMbstringSupport ? \mb_strpos($string, $char) : \strpos($string, $char);
return ($position !== false) ? $position : -1;
}
@ -64,7 +64,7 @@ class StringHelper
*/
public function getCharLastOccurrencePosition($char, $string)
{
$position = $this->hasMbstringSupport ? mb_strrpos($string, $char) : strrpos($string, $char);
$position = $this->hasMbstringSupport ? \mb_strrpos($string, $char) : \strrpos($string, $char);
return ($position !== false) ? $position : -1;
}

View file

@ -46,7 +46,7 @@ abstract class OptionsManagerAbstract implements OptionsManagerInterface
*/
public function setOption($optionName, $optionValue)
{
if (in_array($optionName, $this->supportedOptions)) {
if (\in_array($optionName, $this->supportedOptions)) {
$this->options[$optionName] = $optionValue;
}
}

View file

@ -89,7 +89,7 @@ class InternalEntityFactory implements InternalEntityFactoryInterface
*/
public function createRowFromArray(array $cellValues = [])
{
$cells = array_map(function ($cellValue) {
$cells = \array_map(function ($cellValue) {
return $this->createCell($cellValue);
}, $cellValues);

View file

@ -84,8 +84,8 @@ class Reader extends ReaderAbstract
*/
protected function openReader($filePath)
{
$this->originalAutoDetectLineEndings = ini_get('auto_detect_line_endings');
ini_set('auto_detect_line_endings', '1');
$this->originalAutoDetectLineEndings = \ini_get('auto_detect_line_endings');
\ini_set('auto_detect_line_endings', '1');
$this->filePointer = $this->globalFunctionsHelper->fopen($filePath, 'r');
if (!$this->filePointer) {
@ -123,6 +123,6 @@ class Reader extends ReaderAbstract
$this->globalFunctionsHelper->fclose($this->filePointer);
}
ini_set('auto_detect_line_endings', $this->originalAutoDetectLineEndings);
\ini_set('auto_detect_line_endings', $this->originalAutoDetectLineEndings);
}
}

View file

@ -147,7 +147,7 @@ class RowIterator implements IteratorInterface
if ($rowData !== false) {
// str_replace will replace NULL values by empty strings
$rowDataBufferAsArray = str_replace(null, null, $rowData);
$rowDataBufferAsArray = \str_replace(null, null, $rowData);
$this->rowBuffer = $this->entityFactory->createRowFromArray($rowDataBufferAsArray);
$this->numReadRows++;
} else {
@ -193,13 +193,13 @@ class RowIterator implements IteratorInterface
case EncodingHelper::ENCODING_UTF16_LE:
case EncodingHelper::ENCODING_UTF32_LE:
// remove whitespace from the beginning of a string as fgetcsv() add extra whitespace when it try to explode non UTF-8 data
$cellValue = ltrim($cellValue);
$cellValue = \ltrim($cellValue);
break;
case EncodingHelper::ENCODING_UTF16_BE:
case EncodingHelper::ENCODING_UTF32_BE:
// remove whitespace from the end of a string as fgetcsv() add extra whitespace when it try to explode non UTF-8 data
$cellValue = rtrim($cellValue);
$cellValue = \rtrim($cellValue);
break;
}
@ -215,7 +215,7 @@ class RowIterator implements IteratorInterface
*/
protected function isEmptyLine($lineData)
{
return (is_array($lineData) && count($lineData) === 1 && $lineData[0] === null);
return (\is_array($lineData) && \count($lineData) === 1 && $lineData[0] === null);
}
/**

View file

@ -37,7 +37,7 @@ class ReaderFactory
*/
public static function createFromFile(string $path)
{
$extension = strtolower(pathinfo($path, PATHINFO_EXTENSION));
$extension = \strtolower(\pathinfo($path, PATHINFO_EXTENSION));
return self::createFromType($extension);
}

View file

@ -47,12 +47,14 @@ class RowManager
*/
public function fillMissingIndexesWithEmptyCells(Row $row)
{
if ($row->getNumCells() === 0) {
$numCells = $row->getNumCells();
if ($numCells === 0) {
return $row;
}
$rowCells = $row->getCells();
$maxCellIndex = max(array_keys($rowCells));
$maxCellIndex = $numCells;
for ($cellIndex = 0; $cellIndex < $maxCellIndex; $cellIndex++) {
if (!isset($rowCells[$cellIndex])) {

View file

@ -73,7 +73,7 @@ class XMLProcessor
{
$callbackObject = $callback[0];
$callbackMethodName = $callback[1];
$reflectionMethod = new \ReflectionMethod(get_class($callbackObject), $callbackMethodName);
$reflectionMethod = new \ReflectionMethod(\get_class($callbackObject), $callbackMethodName);
$reflectionMethod->setAccessible(true);
return [

View file

@ -22,9 +22,11 @@ class CellValueFormatter
/** Definition of XML nodes names used to parse data */
const XML_NODE_P = 'p';
const XML_NODE_S = 'text:s';
const XML_NODE_A = 'text:a';
const XML_NODE_SPAN = 'text:span';
const XML_NODE_TEXT_A = 'text:a';
const XML_NODE_TEXT_SPAN = 'text:span';
const XML_NODE_TEXT_S = 'text:s';
const XML_NODE_TEXT_TAB = 'text:tab';
const XML_NODE_TEXT_LINE_BREAK = 'text:line-break';
/** Definition of XML attributes used to parse data */
const XML_ATTRIBUTE_TYPE = 'office:value-type';
@ -41,6 +43,13 @@ class CellValueFormatter
/** @var \Box\Spout\Common\Helper\Escaper\ODS Used to unescape XML data */
protected $escaper;
/** @var array List of XML nodes representing whitespaces and their corresponding value */
private static $WHITESPACE_XML_NODES = [
self::XML_NODE_TEXT_S => ' ',
self::XML_NODE_TEXT_TAB => "\t",
self::XML_NODE_TEXT_LINE_BREAK => "\n",
];
/**
* @param bool $shouldFormatDates Whether date/time values should be returned as PHP objects or be formatted as strings
* @param \Box\Spout\Common\Helper\Escaper\ODS $escaper Used to unescape XML data
@ -96,29 +105,71 @@ class CellValueFormatter
$pNodes = $node->getElementsByTagName(self::XML_NODE_P);
foreach ($pNodes as $pNode) {
$currentPValue = '';
foreach ($pNode->childNodes as $childNode) {
if ($childNode instanceof \DOMText) {
$currentPValue .= $childNode->nodeValue;
} elseif ($childNode->nodeName === self::XML_NODE_S) {
$spaceAttribute = $childNode->getAttribute(self::XML_ATTRIBUTE_C);
$numSpaces = (!empty($spaceAttribute)) ? (int) $spaceAttribute : 1;
$currentPValue .= str_repeat(' ', $numSpaces);
} elseif ($childNode->nodeName === self::XML_NODE_A || $childNode->nodeName === self::XML_NODE_SPAN) {
$currentPValue .= $childNode->nodeValue;
}
}
$pNodeValues[] = $currentPValue;
$pNodeValues[] = $this->extractTextValueFromNode($pNode);
}
$escapedCellValue = implode("\n", $pNodeValues);
$escapedCellValue = \implode("\n", $pNodeValues);
$cellValue = $this->escaper->unescape($escapedCellValue);
return $cellValue;
}
/**
* @param $pNode
* @return string
*/
private function extractTextValueFromNode($pNode)
{
$textValue = '';
foreach ($pNode->childNodes as $childNode) {
if ($childNode instanceof \DOMText) {
$textValue .= $childNode->nodeValue;
} elseif ($this->isWhitespaceNode($childNode->nodeName)) {
$textValue .= $this->transformWhitespaceNode($childNode);
} elseif ($childNode->nodeName === self::XML_NODE_TEXT_A || $childNode->nodeName === self::XML_NODE_TEXT_SPAN) {
$textValue .= $this->extractTextValueFromNode($childNode);
}
}
return $textValue;
}
/**
* Returns whether the given node is a whitespace node. It must be one of these:
* - <text:s />
* - <text:tab />
* - <text:line-break />
*
* @param string $nodeName
* @return bool
*/
private function isWhitespaceNode($nodeName)
{
return isset(self::$WHITESPACE_XML_NODES[$nodeName]);
}
/**
* The "<text:p>" node can contain the string value directly
* or contain child elements. In this case, whitespaces contain in
* the child elements should be replaced by their XML equivalent:
* - space => <text:s />
* - tab => <text:tab />
* - line break => <text:line-break />
*
* @see https://docs.oasis-open.org/office/v1.2/os/OpenDocument-v1.2-os-part1.html#__RefHeading__1415200_253892949
*
* @param \DOMNode $node The XML node representing a whitespace
* @return string The corresponding whitespace value
*/
private function transformWhitespaceNode($node)
{
$countAttribute = $node->getAttribute(self::XML_ATTRIBUTE_C); // only defined for "<text:s>"
$numWhitespaces = (!empty($countAttribute)) ? (int) $countAttribute : 1;
return \str_repeat(self::$WHITESPACE_XML_NODES[$node->nodeName], $numWhitespaces);
}
/**
* Returns the cell Numeric value from the given node.
*

View file

@ -145,7 +145,7 @@ abstract class ReaderAbstract implements ReaderInterface
}
// Need to use realpath to fix "Can't open file" on some Windows setup
return realpath($filePath);
return \realpath($filePath);
}
/**
@ -158,7 +158,7 @@ abstract class ReaderAbstract implements ReaderInterface
protected function getStreamWrapperScheme($filePath)
{
$streamScheme = null;
if (preg_match('/^(\w+):\/\//', $filePath, $matches)) {
if (\preg_match('/^(\w+):\/\//', $filePath, $matches)) {
$streamScheme = $matches[1];
}
@ -190,7 +190,7 @@ abstract class ReaderAbstract implements ReaderInterface
$streamScheme = $this->getStreamWrapperScheme($filePath);
return ($streamScheme !== null) ?
in_array($streamScheme, $this->globalFunctionsHelper->stream_get_wrappers()) :
\in_array($streamScheme, $this->globalFunctionsHelper->stream_get_wrappers()) :
true;
}

View file

@ -20,8 +20,8 @@ trait XMLInternalErrorsHelper
*/
protected function useXMLInternalErrors()
{
libxml_clear_errors();
$this->initialUseInternalErrorsValue = libxml_use_internal_errors(true);
\libxml_clear_errors();
$this->initialUseInternalErrorsValue = \libxml_use_internal_errors(true);
}
/**
@ -48,7 +48,7 @@ trait XMLInternalErrorsHelper
*/
private function hasXMLErrorOccured()
{
return (libxml_get_last_error() !== false);
return (\libxml_get_last_error() !== false);
}
/**
@ -60,10 +60,10 @@ trait XMLInternalErrorsHelper
private function getLastXMLErrorMessage()
{
$errorMessage = null;
$error = libxml_get_last_error();
$error = \libxml_get_last_error();
if ($error !== false) {
$errorMessage = trim($error->message);
$errorMessage = \trim($error->message);
}
return $errorMessage;
@ -74,6 +74,6 @@ trait XMLInternalErrorsHelper
*/
protected function resetXMLInternalErrorsSetting()
{
libxml_use_internal_errors($this->initialUseInternalErrorsValue);
\libxml_use_internal_errors($this->initialUseInternalErrorsValue);
}
}

View file

@ -46,9 +46,9 @@ class XMLReader extends \XMLReader
public function getRealPathURIForFileInZip($zipFilePath, $fileInsideZipPath)
{
// The file path should not start with a '/', otherwise it won't be found
$fileInsideZipPathWithoutLeadingSlash = ltrim($fileInsideZipPath, '/');
$fileInsideZipPathWithoutLeadingSlash = \ltrim($fileInsideZipPath, '/');
return (self::ZIP_WRAPPER . realpath($zipFilePath) . '#' . $fileInsideZipPathWithoutLeadingSlash);
return (self::ZIP_WRAPPER . \realpath($zipFilePath) . '#' . $fileInsideZipPathWithoutLeadingSlash);
}
/**
@ -62,7 +62,7 @@ class XMLReader extends \XMLReader
$doesFileExists = false;
$pattern = '/zip:\/\/([^#]+)#(.*)/';
if (preg_match($pattern, $zipStreamURI, $matches)) {
if (\preg_match($pattern, $zipStreamURI, $matches)) {
$zipFilePath = $matches[1];
$innerFilePath = $matches[2];
@ -158,7 +158,7 @@ class XMLReader extends \XMLReader
// In some cases, the node has a prefix (for instance, "<sheet>" can also be "<x:sheet>").
// So if the given node name does not have a prefix, we need to look at the unprefixed name ("localName").
// @see https://github.com/box/spout/issues/233
$hasPrefix = (strpos($nodeName, ':') !== false);
$hasPrefix = (\strpos($nodeName, ':') !== false);
$currentNodeName = ($hasPrefix) ? $this->name : $this->localName;
return ($this->nodeType === $nodeType && $currentNodeName === $nodeName);

View file

@ -37,7 +37,7 @@ class CellHelper
$columnIndex = 0;
// Remove row information
$columnLetters = preg_replace('/\d/', '', $cellIndex);
$columnLetters = \preg_replace('/\d/', '', $cellIndex);
// strlen() is super slow too... Using isset() is way faster and not too unreadable,
// since we checked before that there are between 1 and 3 letters.
@ -75,6 +75,6 @@ class CellHelper
*/
protected static function isValidCellIndex($cellIndex)
{
return (preg_match('/^[A-Z]{1,3}\d+$/', $cellIndex) === 1);
return (\preg_match('/^[A-Z]{1,3}\d+$/', $cellIndex) === 1);
}
}

View file

@ -163,7 +163,7 @@ class CellValueFormatter
*/
protected function formatStrCellValue($nodeValue)
{
$escapedCellValue = trim($nodeValue);
$escapedCellValue = \trim($nodeValue);
$cellValue = $this->escaper->unescape($escapedCellValue);
return $cellValue;
@ -248,8 +248,8 @@ class CellValueFormatter
$baseDate = $this->shouldUse1904Dates ? '1904-01-01' : '1899-12-30';
$daysSinceBaseDate = (int) $nodeValue;
$timeRemainder = fmod($nodeValue, 1);
$secondsRemainder = round($timeRemainder * self::NUM_SECONDS_IN_ONE_DAY, 0);
$timeRemainder = \fmod($nodeValue, 1);
$secondsRemainder = \round($timeRemainder * self::NUM_SECONDS_IN_ONE_DAY, 0);
$dateObj = \DateTime::createFromFormat('|Y-m-d', $baseDate);
$dateObj->modify('+' . $daysSinceBaseDate . 'days');

View file

@ -62,13 +62,13 @@ class DateFormatHelper
// Remove brackets potentially present at the beginning of the format string
// and text portion of the format at the end of it (starting with ";")
// See §18.8.31 of ECMA-376 for more detail.
$dateFormat = preg_replace('/^(?:\[\$[^\]]+?\])?([^;]*).*/', '$1', $excelDateFormat);
$dateFormat = \preg_replace('/^(?:\[\$[^\]]+?\])?([^;]*).*/', '$1', $excelDateFormat);
// Double quotes are used to escape characters that must not be interpreted.
// For instance, ["Day " dd] should result in "Day 13" and we should not try to interpret "D", "a", "y"
// By exploding the format string using double quote as a delimiter, we can get all parts
// that must be transformed (even indexes) and all parts that must not be (odd indexes).
$dateFormatParts = explode('"', $dateFormat);
$dateFormatParts = \explode('"', $dateFormat);
foreach ($dateFormatParts as $partIndex => $dateFormatPart) {
// do not look at odd indexes
@ -77,19 +77,19 @@ class DateFormatHelper
}
// Make sure all characters are lowercase, as the mapping table is using lowercase characters
$transformedPart = strtolower($dateFormatPart);
$transformedPart = \strtolower($dateFormatPart);
// Remove escapes related to non-format characters
$transformedPart = str_replace('\\', '', $transformedPart);
$transformedPart = \str_replace('\\', '', $transformedPart);
// Apply general transformation first...
$transformedPart = strtr($transformedPart, self::$excelDateFormatToPHPDateFormatMapping[self::KEY_GENERAL]);
$transformedPart = \strtr($transformedPart, self::$excelDateFormatToPHPDateFormatMapping[self::KEY_GENERAL]);
// ... then apply hour transformation, for 12-hour or 24-hour format
if (self::has12HourFormatMarker($dateFormatPart)) {
$transformedPart = strtr($transformedPart, self::$excelDateFormatToPHPDateFormatMapping[self::KEY_HOUR_12]);
$transformedPart = \strtr($transformedPart, self::$excelDateFormatToPHPDateFormatMapping[self::KEY_HOUR_12]);
} else {
$transformedPart = strtr($transformedPart, self::$excelDateFormatToPHPDateFormatMapping[self::KEY_HOUR_24]);
$transformedPart = \strtr($transformedPart, self::$excelDateFormatToPHPDateFormatMapping[self::KEY_HOUR_24]);
}
// overwrite the parts array with the new transformed part
@ -97,16 +97,16 @@ class DateFormatHelper
}
// Merge all transformed parts back together
$phpDateFormat = implode('"', $dateFormatParts);
$phpDateFormat = \implode('"', $dateFormatParts);
// Finally, to have the date format compatible with the DateTime::format() function, we need to escape
// all characters that are inside double quotes (and double quotes must be removed).
// For instance, ["Day " dd] should become [\D\a\y\ dd]
$phpDateFormat = preg_replace_callback('/"(.+?)"/', function ($matches) {
$phpDateFormat = \preg_replace_callback('/"(.+?)"/', function ($matches) {
$stringToEscape = $matches[1];
$letters = preg_split('//u', $stringToEscape, -1, PREG_SPLIT_NO_EMPTY);
$letters = \preg_split('//u', $stringToEscape, -1, PREG_SPLIT_NO_EMPTY);
return '\\' . implode('\\', $letters);
return '\\' . \implode('\\', $letters);
}, $phpDateFormat);
return $phpDateFormat;
@ -118,6 +118,6 @@ class DateFormatHelper
*/
private static function has12HourFormatMarker($excelDateFormat)
{
return (stripos($excelDateFormat, 'am/pm') !== false);
return (\stripos($excelDateFormat, 'am/pm') !== false);
}
}

View file

@ -29,7 +29,7 @@ class OptionsManager extends OptionsManagerAbstract
*/
protected function setDefaultOptions()
{
$this->setOption(Options::TEMP_FOLDER, sys_get_temp_dir());
$this->setOption(Options::TEMP_FOLDER, \sys_get_temp_dir());
$this->setOption(Options::SHOULD_FORMAT_DATES, false);
$this->setOption(Options::SHOULD_PRESERVE_EMPTY_ROWS, false);
$this->setOption(Options::SHOULD_USE_1904_DATES, false);

View file

@ -103,14 +103,14 @@ class CachingStrategyFactory
protected function getMemoryLimitInKB()
{
$memoryLimitFormatted = $this->getMemoryLimitFromIni();
$memoryLimitFormatted = strtolower(trim($memoryLimitFormatted));
$memoryLimitFormatted = \strtolower(\trim($memoryLimitFormatted));
// No memory limit
if ($memoryLimitFormatted === '-1') {
return -1;
}
if (preg_match('/(\d+)([bkmgt])b?/', $memoryLimitFormatted, $matches)) {
if (\preg_match('/(\d+)([bkmgt])b?/', $memoryLimitFormatted, $matches)) {
$amount = (int) ($matches[1]);
$unit = $matches[2];
@ -133,6 +133,6 @@ class CachingStrategyFactory
*/
protected function getMemoryLimitFromIni()
{
return ini_get('memory_limit');
return \ini_get('memory_limit');
}
}

View file

@ -55,7 +55,7 @@ class FileBasedStrategy implements CachingStrategyInterface
public function __construct($tempFolder, $maxNumStringsPerTempFile, $helperFactory)
{
$this->fileSystemHelper = $helperFactory->createFileSystemHelper($tempFolder);
$this->tempFolder = $this->fileSystemHelper->createFolder($tempFolder, uniqid('sharedstrings'));
$this->tempFolder = $this->fileSystemHelper->createFolder($tempFolder, \uniqid('sharedstrings'));
$this->maxNumStringsPerTempFile = $maxNumStringsPerTempFile;
@ -135,7 +135,7 @@ class FileBasedStrategy implements CachingStrategyInterface
// free memory
unset($this->inMemoryTempFileContents);
$this->inMemoryTempFileContents = explode(PHP_EOL, $this->globalFunctionsHelper->file_get_contents($tempFilePath));
$this->inMemoryTempFileContents = \explode(PHP_EOL, $this->globalFunctionsHelper->file_get_contents($tempFilePath));
$this->inMemoryTempFilePath = $tempFilePath;
}
@ -151,7 +151,7 @@ class FileBasedStrategy implements CachingStrategyInterface
throw new SharedStringNotFoundException("Shared string not found for index: $sharedStringIndex");
}
return rtrim($sharedString, PHP_EOL);
return \rtrim($sharedString, PHP_EOL);
}
/**
@ -162,7 +162,7 @@ class FileBasedStrategy implements CachingStrategyInterface
*/
private function escapeLineFeed($unescapedString)
{
return str_replace("\n", self::ESCAPED_LINE_FEED_CHARACTER, $unescapedString);
return \str_replace("\n", self::ESCAPED_LINE_FEED_CHARACTER, $unescapedString);
}
/**
@ -173,7 +173,7 @@ class FileBasedStrategy implements CachingStrategyInterface
*/
private function unescapeLineFeed($escapedString)
{
return str_replace(self::ESCAPED_LINE_FEED_CHARACTER, "\n", $escapedString);
return \str_replace(self::ESCAPED_LINE_FEED_CHARACTER, "\n", $escapedString);
}
/**

View file

@ -190,7 +190,7 @@ class SharedStringsManager
$textNodeValue = $textNode->nodeValue;
$shouldPreserveWhitespace = $this->shouldPreserveWhitespace($textNode);
$sharedStringValue .= ($shouldPreserveWhitespace) ? $textNodeValue : trim($textNodeValue);
$sharedStringValue .= ($shouldPreserveWhitespace) ? $textNodeValue : \trim($textNodeValue);
}
}

View file

@ -116,7 +116,7 @@ class SheetManager
{
// Using "filter_var($x, FILTER_VALIDATE_BOOLEAN)" here because the value of the "date1904" attribute
// may be the string "false", that is not mapped to the boolean "false" by default...
$shouldUse1904Dates = filter_var($xmlReader->getAttribute(self::XML_ATTRIBUTE_DATE_1904), FILTER_VALIDATE_BOOLEAN);
$shouldUse1904Dates = \filter_var($xmlReader->getAttribute(self::XML_ATTRIBUTE_DATE_1904), FILTER_VALIDATE_BOOLEAN);
$this->optionsManager->setOption(Options::SHOULD_USE_1904_DATES, $shouldUse1904Dates);
return XMLProcessor::PROCESSING_CONTINUE;
@ -211,7 +211,7 @@ class SheetManager
$sheetDataXMLFilePath = $xmlReader->getAttribute(self::XML_ATTRIBUTE_TARGET);
// sometimes, the sheet data file path already contains "/xl/"...
if (strpos($sheetDataXMLFilePath, '/xl/') !== 0) {
if (\strpos($sheetDataXMLFilePath, '/xl/') !== 0) {
$sheetDataXMLFilePath = '/xl/' . $sheetDataXMLFilePath;
break;
}

View file

@ -48,7 +48,10 @@ class StyleManager
/** @var string Path of the XLSX file being read */
protected $filePath;
/** @var string Path of the styles XML file */
/** @var bool Whether the XLSX file contains a styles XML file */
protected $hasStylesXMLFile;
/** @var string|null Path of the styles XML file */
protected $stylesXMLFilePath;
/** @var InternalEntityFactory Factory to create entities */
@ -75,8 +78,11 @@ class StyleManager
{
$this->filePath = $filePath;
$this->entityFactory = $entityFactory;
$this->builtinNumFmtIdIndicatingDates = array_keys(self::$builtinNumFmtIdToNumFormatMapping);
$this->stylesXMLFilePath = $workbookRelationshipsManager->getStylesXMLFilePath();
$this->builtinNumFmtIdIndicatingDates = \array_keys(self::$builtinNumFmtIdToNumFormatMapping);
$this->hasStylesXMLFile = $workbookRelationshipsManager->hasStylesXMLFile();
if ($this->hasStylesXMLFile) {
$this->stylesXMLFilePath = $workbookRelationshipsManager->getStylesXMLFilePath();
}
}
/**
@ -88,6 +94,10 @@ class StyleManager
*/
public function shouldFormatNumericValueAsDate($styleId)
{
if (!$this->hasStylesXMLFile) {
return false;
}
$stylesAttributes = $this->getStylesAttributes();
// Default style (0) does not format numeric values as timestamps. Only custom styles do.
@ -263,7 +273,7 @@ class StyleManager
*/
protected function isNumFmtIdBuiltInDateFormat($numFmtId)
{
return in_array($numFmtId, $this->builtinNumFmtIdIndicatingDates);
return \in_array($numFmtId, $this->builtinNumFmtIdIndicatingDates);
}
/**
@ -273,7 +283,7 @@ class StyleManager
protected function isFormatCodeCustomDateFormat($formatCode)
{
// if no associated format code or if using the default "General" format
if ($formatCode === null || strcasecmp($formatCode, self::NUMBER_FORMAT_GENERAL) === 0) {
if ($formatCode === null || \strcasecmp($formatCode, self::NUMBER_FORMAT_GENERAL) === 0) {
return false;
}
@ -288,7 +298,7 @@ class StyleManager
{
// Remove extra formatting (what's between [ ], the brackets should not be preceded by a "\")
$pattern = '((?<!\\\)\[.+?(?<!\\\)\])';
$formatCode = preg_replace($pattern, '', $formatCode);
$formatCode = \preg_replace($pattern, '', $formatCode);
// custom date formats contain specific characters to represent the date:
// e - yy - m - d - h - s
@ -300,7 +310,7 @@ class StyleManager
// character not preceded by "\" (case insensitive)
$pattern = '/(?<!\\\)' . $dateFormatCharacter . '/i';
if (preg_match($pattern, $formatCode)) {
if (\preg_match($pattern, $formatCode)) {
$hasFoundDateFormatCharacter = true;
break;
}

View file

@ -55,7 +55,7 @@ class WorkbookRelationshipsManager
$sharedStringsXMLFilePath = $workbookRelationships[self::RELATIONSHIP_TYPE_SHARED_STRINGS];
// the file path can be relative (e.g. "styles.xml") or absolute (e.g. "/xl/styles.xml")
$doesContainBasePath = (strpos($sharedStringsXMLFilePath, self::BASE_PATH) !== false);
$doesContainBasePath = (\strpos($sharedStringsXMLFilePath, self::BASE_PATH) !== false);
if (!$doesContainBasePath) {
// make sure we return an absolute file path
$sharedStringsXMLFilePath = self::BASE_PATH . $sharedStringsXMLFilePath;
@ -75,7 +75,17 @@ class WorkbookRelationshipsManager
}
/**
* @return string|null The path of the styles XML file
* @return bool Whether the XLSX file contains a styles XML file
*/
public function hasStylesXMLFile()
{
$workbookRelationships = $this->getWorkbookRelationships();
return isset($workbookRelationships[self::RELATIONSHIP_TYPE_STYLES]);
}
/**
* @return string The path of the styles XML file
*/
public function getStylesXMLFilePath()
{
@ -83,7 +93,7 @@ class WorkbookRelationshipsManager
$stylesXMLFilePath = $workbookRelationships[self::RELATIONSHIP_TYPE_STYLES];
// the file path can be relative (e.g. "styles.xml") or absolute (e.g. "/xl/styles.xml")
$doesContainBasePath = (strpos($stylesXMLFilePath, self::BASE_PATH) !== false);
$doesContainBasePath = (\strpos($stylesXMLFilePath, self::BASE_PATH) !== false);
if (!$doesContainBasePath) {
// make sure we return a full path
$stylesXMLFilePath = self::BASE_PATH . $stylesXMLFilePath;

View file

@ -127,7 +127,7 @@ class RowIterator implements IteratorInterface
*/
protected function normalizeSheetDataXMLFilePath($sheetDataXMLFilePath)
{
return ltrim($sheetDataXMLFilePath, '/');
return \ltrim($sheetDataXMLFilePath, '/');
}
/**
@ -234,7 +234,7 @@ class RowIterator implements IteratorInterface
{
// Read dimensions of the sheet
$dimensionRef = $xmlReader->getAttribute(self::XML_ATTRIBUTE_REF); // returns 'A1:M13' for instance (or 'A1' for empty sheet)
if (preg_match('/[A-Z]+\d+:([A-Z]+\d+)/', $dimensionRef, $matches)) {
if (\preg_match('/[A-Z]+\d+:([A-Z]+\d+)/', $dimensionRef, $matches)) {
$this->numColumns = CellHelper::getColumnIndexFromCellIndex($matches[1]) + 1;
}
@ -257,11 +257,11 @@ class RowIterator implements IteratorInterface
$numberOfColumnsForRow = $this->numColumns;
$spans = $xmlReader->getAttribute(self::XML_ATTRIBUTE_SPANS); // returns '1:5' for instance
if ($spans) {
list(, $numberOfColumnsForRow) = explode(':', $spans);
list(, $numberOfColumnsForRow) = \explode(':', $spans);
$numberOfColumnsForRow = (int) $numberOfColumnsForRow;
}
$cells = array_fill(0, $numberOfColumnsForRow, $this->entityFactory->createCell(''));
$cells = \array_fill(0, $numberOfColumnsForRow, $this->entityFactory->createCell(''));
$this->currentlyProcessedRow->setCells($cells);
return XMLProcessor::PROCESSING_CONTINUE;

View file

@ -27,7 +27,7 @@ class SheetIterator implements IteratorInterface
// Fetch all available sheets
$this->sheets = $sheetManager->getSheets();
if (count($this->sheets) === 0) {
if (\count($this->sheets) === 0) {
throw new NoSheetsFoundException('The file must contain at least one sheet.');
}
}
@ -51,7 +51,7 @@ class SheetIterator implements IteratorInterface
*/
public function valid()
{
return ($this->currentSheetIndex < count($this->sheets));
return ($this->currentSheetIndex < \count($this->sheets));
}
/**

View file

@ -3,7 +3,9 @@
namespace Box\Spout\Writer\Common\Creator\Style;
use Box\Spout\Common\Entity\Style\Border;
use Box\Spout\Common\Entity\Style\CellAlignment;
use Box\Spout\Common\Entity\Style\Style;
use Box\Spout\Common\Exception\InvalidArgumentException;
/**
* Class StyleBuilder
@ -122,6 +124,25 @@ class StyleBuilder
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
*
@ -148,6 +169,20 @@ class StyleBuilder
return $this;
}
/**
* Sets a format
*
* @param string $format Format
* @return StyleBuilder
* @api
*/
public function setFormat($format)
{
$this->style->setFormat($format);
return $this;
}
/**
* Returns the configured style. The style is cached and can be reused.
*

View file

@ -98,7 +98,7 @@ class WriterEntityFactory
*/
public static function createRowFromArray(array $cellValues = [], Style $rowStyle = null)
{
$cells = array_map(function ($cellValue) {
$cells = \array_map(function ($cellValue) {
return new Cell($cellValue);
}, $cellValues);

View file

@ -35,7 +35,7 @@ class WriterFactory
*/
public static function createFromFile(string $path)
{
$extension = strtolower(pathinfo($path, PATHINFO_EXTENSION));
$extension = \strtolower(\pathinfo($path, PATHINFO_EXTENSION));
return self::createFromType($extension);
}

View file

@ -19,7 +19,7 @@ class Workbook
*/
public function __construct()
{
$this->internalId = uniqid();
$this->internalId = \uniqid();
}
/**

View file

@ -8,38 +8,39 @@ namespace Box\Spout\Writer\Common\Helper;
*/
class CellHelper
{
/** @var array Cache containing the mapping column index => cell index */
private static $columnIndexToCellIndexCache = [];
/** @var array Cache containing the mapping column index => column letters */
private static $columnIndexToColumnLettersCache = [];
/**
* Returns the cell index (base 26) associated to the base 10 column index.
* Returns the column letters (base 26) associated to the base 10 column index.
* Excel uses A to Z letters for column indexing, where A is the 1st column,
* Z is the 26th and AA is the 27th.
* The mapping is zero based, so that 0 maps to A, B maps to 1, Z to 25 and AA to 26.
*
* @param int $columnIndex The Excel column index (0, 42, ...)
* @param int $columnIndexZeroBased The Excel column index (0, 42, ...)
*
* @return string The associated cell index ('A', 'BC', ...)
*/
public static function getCellIndexFromColumnIndex($columnIndex)
public static function getColumnLettersFromColumnIndex($columnIndexZeroBased)
{
$originalColumnIndex = $columnIndex;
$originalColumnIndex = $columnIndexZeroBased;
// Using isset here because it is way faster than array_key_exists...
if (!isset(self::$columnIndexToCellIndexCache[$originalColumnIndex])) {
$cellIndex = '';
$capitalAAsciiValue = ord('A');
if (!isset(self::$columnIndexToColumnLettersCache[$originalColumnIndex])) {
$columnLetters = '';
$capitalAAsciiValue = \ord('A');
do {
$modulus = $columnIndex % 26;
$cellIndex = chr($capitalAAsciiValue + $modulus) . $cellIndex;
$modulus = $columnIndexZeroBased % 26;
$columnLetters = \chr($capitalAAsciiValue + $modulus) . $columnLetters;
// substracting 1 because it's zero-based
$columnIndex = (int) ($columnIndex / 26) - 1;
} while ($columnIndex >= 0);
$columnIndexZeroBased = (int) ($columnIndexZeroBased / 26) - 1;
} while ($columnIndexZeroBased >= 0);
self::$columnIndexToCellIndexCache[$originalColumnIndex] = $cellIndex;
self::$columnIndexToColumnLettersCache[$originalColumnIndex] = $columnLetters;
}
return self::$columnIndexToCellIndexCache[$originalColumnIndex];
return self::$columnIndexToColumnLettersCache[$originalColumnIndex];
}
}

View file

@ -135,7 +135,7 @@ class ZipHelper
public static function canChooseCompressionMethod()
{
// setCompressionName() is a PHP7+ method...
return (method_exists(new \ZipArchive(), 'setCompressionName'));
return (\method_exists(new \ZipArchive(), 'setCompressionName'));
}
/**
@ -151,7 +151,7 @@ class ZipHelper
foreach ($itemIterator as $itemInfo) {
$itemRealPath = $this->getNormalizedRealPath($itemInfo->getPathname());
$itemLocalPath = str_replace($folderRealPath, '', $itemRealPath);
$itemLocalPath = \str_replace($folderRealPath, '', $itemRealPath);
if ($itemInfo->isFile() && !$this->shouldSkipFile($zip, $itemLocalPath, $existingFileMode)) {
$zip->addFile($itemRealPath, $itemLocalPath);
@ -181,9 +181,9 @@ class ZipHelper
*/
protected function getNormalizedRealPath($path)
{
$realPath = realpath($path);
$realPath = \realpath($path);
return str_replace(DIRECTORY_SEPARATOR, '/', $realPath);
return \str_replace(DIRECTORY_SEPARATOR, '/', $realPath);
}
/**
@ -210,8 +210,8 @@ class ZipHelper
*/
protected function copyZipToStream($zipFilePath, $pointer)
{
$zipFilePointer = fopen($zipFilePath, 'r');
stream_copy_to_stream($zipFilePointer, $pointer);
fclose($zipFilePointer);
$zipFilePointer = \fopen($zipFilePath, 'r');
\stream_copy_to_stream($zipFilePointer, $pointer);
\fclose($zipFilePointer);
}
}

View file

@ -45,8 +45,8 @@ class SheetManager
*/
public function throwIfNameIsInvalid($name, Sheet $sheet)
{
if (!is_string($name)) {
$actualType = gettype($name);
if (!\is_string($name)) {
$actualType = \gettype($name);
$errorMessage = "The sheet's name is invalid. It must be a string ($actualType given).";
throw new InvalidSheetNameException($errorMessage);
}
@ -74,9 +74,9 @@ class SheetManager
}
}
if (count($failedRequirements) !== 0) {
if (\count($failedRequirements) !== 0) {
$errorMessage = "The sheet's name (\"$name\") is invalid. It did not respect these rules:\n - ";
$errorMessage .= implode("\n - ", $failedRequirements);
$errorMessage .= \implode("\n - ", $failedRequirements);
throw new InvalidSheetNameException($errorMessage);
}
}
@ -90,7 +90,7 @@ class SheetManager
*/
private function doesContainInvalidCharacters($name)
{
return (str_replace(self::$INVALID_CHARACTERS_IN_SHEET_NAME, '', $name) !== $name);
return (\str_replace(self::$INVALID_CHARACTERS_IN_SHEET_NAME, '', $name) !== $name);
}
/**

View file

@ -80,7 +80,7 @@ class StyleManager implements StyleManagerInterface
return $cellStyle;
}
if ($cell->isString() && strpos($cell->getValue(), "\n") !== false) {
if ($cell->isString() && \strpos($cell->getValue(), "\n") !== false) {
$cellStyle->setShouldWrapText();
}

View file

@ -85,9 +85,15 @@ class StyleMerger
if (!$style->hasSetWrapText() && $baseStyle->shouldWrapText()) {
$styleToUpdate->setShouldWrapText();
}
if (!$style->hasSetCellAlignment() && $baseStyle->shouldApplyCellAlignment()) {
$styleToUpdate->setCellAlignment($baseStyle->getCellAlignment());
}
if (!$style->getBorder() && $baseStyle->shouldApplyBorder()) {
$styleToUpdate->setBorder($baseStyle->getBorder());
}
if (!$style->getFormat() && $baseStyle->shouldApplyFormat()) {
$styleToUpdate->setFormat($baseStyle->getFormat());
}
if (!$style->shouldApplyBackgroundColor() && $baseStyle->shouldApplyBackgroundColor()) {
$styleToUpdate->setBackgroundColor($baseStyle->getBackgroundColor());
}

View file

@ -37,7 +37,7 @@ class StyleRegistry
$serializedStyle = $this->serialize($style);
if (!$this->hasStyleAlreadyBeenRegistered($style)) {
$nextStyleId = count($this->serializedStyleToStyleIdMappingTable);
$nextStyleId = \count($this->serializedStyleToStyleIdMappingTable);
$style->setId($nextStyleId);
$this->serializedStyleToStyleIdMappingTable[$serializedStyle] = $nextStyleId;
@ -79,7 +79,7 @@ class StyleRegistry
*/
public function getRegisteredStyles()
{
return array_values($this->styleIdToStyleMappingTable);
return \array_values($this->styleIdToStyleMappingTable);
}
/**
@ -105,7 +105,7 @@ class StyleRegistry
$currentId = $style->getId();
$style->setId(0);
$serializedStyle = serialize($style);
$serializedStyle = \serialize($style);
$style->setId($currentId);

View file

@ -124,7 +124,7 @@ abstract class WorkbookManagerAbstract implements WorkbookManagerInterface
{
$worksheets = $this->getWorksheets();
$newSheetIndex = count($worksheets);
$newSheetIndex = \count($worksheets);
$sheetManager = $this->managerFactory->createSheetManager();
$sheet = $this->entityFactory->createSheet($newSheetIndex, $this->workbook->getInternalId(), $sheetManager);
@ -260,7 +260,7 @@ abstract class WorkbookManagerAbstract implements WorkbookManagerInterface
// update max num columns for the worksheet
$currentMaxNumColumns = $worksheet->getMaxNumColumns();
$cellsCount = $row->getNumCells();
$worksheet->setMaxNumColumns(max($currentMaxNumColumns, $cellsCount));
$worksheet->setMaxNumColumns(\max($currentMaxNumColumns, $cellsCount));
}
/**

View file

@ -11,6 +11,6 @@ class InvalidNameException extends WriterException
{
$msg = '%s is not a valid name identifier for a border. Valid identifiers are: %s.';
parent::__construct(sprintf($msg, $name, implode(',', BorderPart::getAllowedNames())));
parent::__construct(\sprintf($msg, $name, \implode(',', BorderPart::getAllowedNames())));
}
}

View file

@ -11,6 +11,6 @@ class InvalidStyleException extends WriterException
{
$msg = '%s is not a valid style identifier for a border. Valid identifiers are: %s.';
parent::__construct(sprintf($msg, $name, implode(',', BorderPart::getAllowedStyles())));
parent::__construct(\sprintf($msg, $name, \implode(',', BorderPart::getAllowedStyles())));
}
}

View file

@ -11,6 +11,6 @@ class InvalidWidthException extends WriterException
{
$msg = '%s is not a valid width identifier for a border. Valid identifiers are: %s.';
parent::__construct(sprintf($msg, $name, implode(',', BorderPart::getAllowedWidths())));
parent::__construct(\sprintf($msg, $name, \implode(',', BorderPart::getAllowedWidths())));
}
}

View file

@ -53,14 +53,14 @@ class BorderHelper
$definition = 'fo:border-%s="%s"';
if ($borderPart->getStyle() === Border::STYLE_NONE) {
$borderPartDefinition = sprintf($definition, $borderPart->getName(), 'none');
$borderPartDefinition = \sprintf($definition, $borderPart->getName(), 'none');
} else {
$attributes = [
self::$widthMap[$borderPart->getWidth()],
self::$styleMap[$borderPart->getStyle()],
'#' . $borderPart->getColor(),
];
$borderPartDefinition = sprintf($definition, $borderPart->getName(), implode(' ', $attributes));
$borderPartDefinition = \sprintf($definition, $borderPart->getName(), \implode(' ', $attributes));
}
return $borderPartDefinition;

View file

@ -89,7 +89,7 @@ class FileSystemHelper extends \Box\Spout\Common\Helper\FileSystemHelper impleme
*/
protected function createRootFolder()
{
$this->rootFolder = $this->createFolder($this->baseFolderRealPath, uniqid('ods'));
$this->rootFolder = $this->createFolder($this->baseFolderRealPath, \uniqid('ods'));
return $this;
}
@ -210,22 +210,22 @@ EOD;
// Append sheets content to "content.xml"
$contentXmlFilePath = $this->rootFolder . '/' . self::CONTENT_XML_FILE_NAME;
$contentXmlHandle = fopen($contentXmlFilePath, 'a');
$contentXmlHandle = \fopen($contentXmlFilePath, 'a');
foreach ($worksheets as $worksheet) {
// write the "<table:table>" node, with the final sheet's name
fwrite($contentXmlHandle, $worksheetManager->getTableElementStartAsString($worksheet));
\fwrite($contentXmlHandle, $worksheetManager->getTableElementStartAsString($worksheet));
$worksheetFilePath = $worksheet->getFilePath();
$this->copyFileContentsToTarget($worksheetFilePath, $contentXmlHandle);
fwrite($contentXmlHandle, '</table:table>');
\fwrite($contentXmlHandle, '</table:table>');
}
$contentXmlFileContents = '</office:spreadsheet></office:body></office:document-content>';
fwrite($contentXmlHandle, $contentXmlFileContents);
fclose($contentXmlHandle);
\fwrite($contentXmlHandle, $contentXmlFileContents);
\fclose($contentXmlHandle);
return $this;
}
@ -241,9 +241,9 @@ EOD;
*/
protected function copyFileContentsToTarget($sourceFilePath, $targetResource)
{
$sourceHandle = fopen($sourceFilePath, 'r');
stream_copy_to_stream($sourceHandle, $targetResource);
fclose($sourceHandle);
$sourceHandle = \fopen($sourceFilePath, 'r');
\stream_copy_to_stream($sourceHandle, $targetResource);
\fclose($sourceHandle);
}
/**

View file

@ -42,7 +42,7 @@ class OptionsManager extends OptionsManagerAbstract
*/
protected function setDefaultOptions()
{
$this->setOption(Options::TEMP_FOLDER, sys_get_temp_dir());
$this->setOption(Options::TEMP_FOLDER, \sys_get_temp_dir());
$this->setOption(Options::DEFAULT_ROW_STYLE, $this->styleBuilder->build());
$this->setOption(Options::SHOULD_CREATE_NEW_SHEETS_AUTOMATICALLY, true);
}

View file

@ -3,6 +3,7 @@
namespace Box\Spout\Writer\ODS\Manager\Style;
use Box\Spout\Common\Entity\Style\BorderPart;
use Box\Spout\Common\Entity\Style\CellAlignment;
use Box\Spout\Writer\Common\Entity\Worksheet;
use Box\Spout\Writer\ODS\Helper\BorderHelper;
@ -199,6 +200,7 @@ EOD;
$content = '<style:style style:data-style-name="N0" style:family="table-cell" style:name="ce' . $styleIndex . '" style:parent-style-name="Default">';
$content .= $this->getTextPropertiesSectionContent($style);
$content .= $this->getParagraphPropertiesSectionContent($style);
$content .= $this->getTableCellPropertiesSectionContent($style);
$content .= '</style:style>';
@ -214,26 +216,26 @@ EOD;
*/
private function getTextPropertiesSectionContent($style)
{
$content = '';
if ($style->shouldApplyFont()) {
$content .= $this->getFontSectionContent($style);
if (!$style->shouldApplyFont()) {
return '';
}
return $content;
return '<style:text-properties '
. $this->getFontSectionContent($style)
. '/>';
}
/**
* Returns the contents of the "<style:text-properties>" section, inside "<style:style>" section
* Returns the contents of the fonts definition section, inside "<style:text-properties>" section
*
* @param \Box\Spout\Common\Entity\Style\Style $style
*
* @return string
*/
private function getFontSectionContent($style)
{
$defaultStyle = $this->getDefaultStyle();
$content = '<style:text-properties';
$content = '';
$fontColor = $style->getFontColor();
if ($fontColor !== $defaultStyle->getFontColor()) {
@ -263,11 +265,60 @@ EOD;
$content .= ' style:text-line-through-style="solid"';
}
$content .= '/>';
return $content;
}
/**
* Returns the contents of the "<style:paragraph-properties>" section, inside "<style:style>" section
*
* @param \Box\Spout\Common\Entity\Style\Style $style
*
* @return string
*/
private function getParagraphPropertiesSectionContent($style)
{
if (!$style->shouldApplyCellAlignment()) {
return '';
}
return '<style:paragraph-properties '
. $this->getCellAlignmentSectionContent($style)
. '/>';
}
/**
* Returns the contents of the cell alignment definition for the "<style:paragraph-properties>" section
*
* @param \Box\Spout\Common\Entity\Style\Style $style
*
* @return string
*/
private function getCellAlignmentSectionContent($style)
{
return \sprintf(
' fo:text-align="%s" ',
$this->transformCellAlignment($style->getCellAlignment())
);
}
/**
* Even though "left" and "right" alignments are part of the spec, and interpreted
* respectively as "start" and "end", using the recommended values increase compatibility
* with software that will read the created ODS file.
*
* @param string $cellAlignment
*
* @return string
*/
private function transformCellAlignment($cellAlignment)
{
switch ($cellAlignment) {
case CellAlignment::LEFT: return 'start';
case CellAlignment::RIGHT: return 'end';
default: return $cellAlignment;
}
}
/**
* Returns the contents of the "<style:table-cell-properties>" section, inside "<style:style>" section
*
@ -276,7 +327,7 @@ EOD;
*/
private function getTableCellPropertiesSectionContent($style)
{
$content = '';
$content = '<style:table-cell-properties ';
if ($style->shouldWrapText()) {
$content .= $this->getWrapTextXMLContent();
@ -290,6 +341,8 @@ EOD;
$content .= $this->getBackgroundColorXMLContent($style);
}
$content .= '/>';
return $content;
}
@ -300,7 +353,7 @@ EOD;
*/
private function getWrapTextXMLContent()
{
return '<style:table-cell-properties fo:wrap-option="wrap" style:vertical-align="automatic"/>';
return ' fo:wrap-option="wrap" style:vertical-align="automatic" ';
}
/**
@ -311,13 +364,11 @@ EOD;
*/
private function getBorderXMLContent($style)
{
$borderProperty = '<style:table-cell-properties %s />';
$borders = array_map(function (BorderPart $borderPart) {
$borders = \array_map(function (BorderPart $borderPart) {
return BorderHelper::serializeBorderPart($borderPart);
}, $style->getBorder()->getParts());
return sprintf($borderProperty, implode(' ', $borders));
return \sprintf(' %s ', \implode(' ', $borders));
}
/**
@ -328,9 +379,6 @@ EOD;
*/
private function getBackgroundColorXMLContent($style)
{
return sprintf(
'<style:table-cell-properties fo:background-color="#%s"/>',
$style->getBackgroundColor()
);
return \sprintf(' fo:background-color="#%s" ', $style->getBackgroundColor());
}
}

View file

@ -33,6 +33,6 @@ class StyleRegistry extends \Box\Spout\Writer\Common\Manager\Style\StyleRegistry
*/
public function getUsedFonts()
{
return array_keys($this->usedFontsSet);
return \array_keys($this->usedFontsSet);
}
}

View file

@ -56,7 +56,7 @@ class WorkbookManager extends WorkbookManagerAbstract
protected function writeAllFilesToDiskAndZipThem($finalFilePointer)
{
$worksheets = $this->getWorksheets();
$numWorksheets = count($worksheets);
$numWorksheets = \count($worksheets);
$this->fileSystemHelper
->createContentFile($this->worksheetManager, $this->styleManager, $worksheets)

View file

@ -61,7 +61,7 @@ class WorksheetManager implements WorksheetManagerInterface
*/
public function startSheet(Worksheet $worksheet)
{
$sheetFilePointer = fopen($worksheet->getFilePath(), 'w');
$sheetFilePointer = \fopen($worksheet->getFilePath(), 'w');
$this->throwIfSheetFilePointerIsNotAvailable($sheetFilePointer);
$worksheet->setFilePointer($sheetFilePointer);
@ -134,7 +134,7 @@ class WorksheetManager implements WorksheetManagerInterface
$data .= '</table:table-row>';
$wasWriteSuccessful = fwrite($worksheet->getFilePointer(), $data);
$wasWriteSuccessful = \fwrite($worksheet->getFilePointer(), $data);
if ($wasWriteSuccessful === false) {
throw new IOException("Unable to write data in {$worksheet->getFilePath()}");
}
@ -190,7 +190,7 @@ class WorksheetManager implements WorksheetManagerInterface
if ($cell->isString()) {
$data .= ' office:value-type="string" calcext:value-type="string">';
$cellValueLines = explode("\n", $cell->getValue());
$cellValueLines = \explode("\n", $cell->getValue());
foreach ($cellValueLines as $cellValueLine) {
$data .= '<text:p>' . $this->stringsEscaper->escape($cellValueLine) . '</text:p>';
}
@ -204,10 +204,15 @@ class WorksheetManager implements WorksheetManagerInterface
$data .= ' office:value-type="float" calcext:value-type="float" office:value="' . $cell->getValue() . '">';
$data .= '<text:p>' . $cell->getValue() . '</text:p>';
$data .= '</table:table-cell>';
} elseif ($cell->isError() && is_string($cell->getValueEvenIfError())) {
// only writes the error value if it's a string
$data .= ' office:value-type="string" calcext:value-type="error" office:value="">';
$data .= '<text:p>' . $cell->getValueEvenIfError() . '</text:p>';
$data .= '</table:table-cell>';
} elseif ($cell->isEmpty()) {
$data .= '/>';
} else {
throw new InvalidArgumentException('Trying to add a value with an unsupported type: ' . gettype($cell->getValue()));
throw new InvalidArgumentException('Trying to add a value with an unsupported type: ' . \gettype($cell->getValue()));
}
return $data;
@ -223,10 +228,10 @@ class WorksheetManager implements WorksheetManagerInterface
{
$worksheetFilePointer = $worksheet->getFilePointer();
if (!is_resource($worksheetFilePointer)) {
if (!\is_resource($worksheetFilePointer)) {
return;
}
fclose($worksheetFilePointer);
\fclose($worksheetFilePointer);
}
}

View file

@ -223,7 +223,7 @@ abstract class WriterAbstract implements WriterInterface
$this->closeWriter();
if (is_resource($this->filePointer)) {
if (\is_resource($this->filePointer)) {
$this->globalFunctionsHelper->fclose($this->filePointer);
}
@ -243,7 +243,7 @@ abstract class WriterAbstract implements WriterInterface
// remove output file if it was created
if ($this->globalFunctionsHelper->file_exists($this->outputFilePath)) {
$outputFolderPath = dirname($this->outputFilePath);
$outputFolderPath = \dirname($this->outputFilePath);
$fileSystemHelper = $this->helperFactory->createFileSystemHelper($outputFolderPath);
$fileSystemHelper->deleteFile($this->outputFilePath);
}

View file

@ -43,8 +43,8 @@ class BorderHelper
{
$borderStyle = self::getBorderStyle($borderPart);
$colorEl = $borderPart->getColor() ? sprintf('<color rgb="%s"/>', $borderPart->getColor()) : '';
$partEl = sprintf(
$colorEl = $borderPart->getColor() ? \sprintf('<color rgb="%s"/>', $borderPart->getColor()) : '';
$partEl = \sprintf(
'<%s style="%s">%s</%s>',
$borderPart->getName(),
$borderStyle,

View file

@ -112,7 +112,7 @@ class FileSystemHelper extends \Box\Spout\Common\Helper\FileSystemHelper impleme
*/
private function createRootFolder()
{
$this->rootFolder = $this->createFolder($this->baseFolderRealPath, uniqid('xlsx', true));
$this->rootFolder = $this->createFolder($this->baseFolderRealPath, \uniqid('xlsx', true));
return $this;
}

View file

@ -52,7 +52,7 @@ class OptionsManager extends OptionsManagerAbstract
->setFontName(self::DEFAULT_FONT_NAME)
->build();
$this->setOption(Options::TEMP_FOLDER, sys_get_temp_dir());
$this->setOption(Options::TEMP_FOLDER, \sys_get_temp_dir());
$this->setOption(Options::DEFAULT_ROW_STYLE, $defaultRowStyle);
$this->setOption(Options::SHOULD_CREATE_NEW_SHEETS_AUTOMATICALLY, true);
$this->setOption(Options::SHOULD_USE_INLINE_STRINGS, true);

View file

@ -40,13 +40,13 @@ EOD;
public function __construct($xlFolder, $stringsEscaper)
{
$sharedStringsFilePath = $xlFolder . '/' . self::SHARED_STRINGS_FILE_NAME;
$this->sharedStringsFilePointer = fopen($sharedStringsFilePath, 'w');
$this->sharedStringsFilePointer = \fopen($sharedStringsFilePath, 'w');
$this->throwIfSharedStringsFilePointerIsNotAvailable();
// the headers is split into different parts so that we can fseek and put in the correct count and uniqueCount later
$header = self::SHARED_STRINGS_XML_FILE_FIRST_PART_HEADER . ' ' . self::DEFAULT_STRINGS_COUNT_PART . '>';
fwrite($this->sharedStringsFilePointer, $header);
\fwrite($this->sharedStringsFilePointer, $header);
$this->stringsEscaper = $stringsEscaper;
}
@ -73,7 +73,7 @@ EOD;
*/
public function writeString($string)
{
fwrite($this->sharedStringsFilePointer, '<si><t xml:space="preserve">' . $this->stringsEscaper->escape($string) . '</t></si>');
\fwrite($this->sharedStringsFilePointer, '<si><t xml:space="preserve">' . $this->stringsEscaper->escape($string) . '</t></si>');
$this->numSharedStrings++;
// Shared string ID is zero-based
@ -87,20 +87,20 @@ EOD;
*/
public function close()
{
if (!is_resource($this->sharedStringsFilePointer)) {
if (!\is_resource($this->sharedStringsFilePointer)) {
return;
}
fwrite($this->sharedStringsFilePointer, '</sst>');
\fwrite($this->sharedStringsFilePointer, '</sst>');
// Replace the default strings count with the actual number of shared strings in the file header
$firstPartHeaderLength = strlen(self::SHARED_STRINGS_XML_FILE_FIRST_PART_HEADER);
$defaultStringsCountPartLength = strlen(self::DEFAULT_STRINGS_COUNT_PART);
$firstPartHeaderLength = \strlen(self::SHARED_STRINGS_XML_FILE_FIRST_PART_HEADER);
$defaultStringsCountPartLength = \strlen(self::DEFAULT_STRINGS_COUNT_PART);
// Adding 1 to take into account the space between the last xml attribute and "count"
fseek($this->sharedStringsFilePointer, $firstPartHeaderLength + 1);
fwrite($this->sharedStringsFilePointer, sprintf("%-{$defaultStringsCountPartLength}s", 'count="' . $this->numSharedStrings . '" uniqueCount="' . $this->numSharedStrings . '"'));
\fseek($this->sharedStringsFilePointer, $firstPartHeaderLength + 1);
\fwrite($this->sharedStringsFilePointer, \sprintf("%-{$defaultStringsCountPartLength}s", 'count="' . $this->numSharedStrings . '" uniqueCount="' . $this->numSharedStrings . '"'));
fclose($this->sharedStringsFilePointer);
\fclose($this->sharedStringsFilePointer);
}
}

View file

@ -33,7 +33,10 @@ class StyleManager extends \Box\Spout\Writer\Common\Manager\Style\StyleManager
$associatedBorderId = $this->styleRegistry->getBorderIdForStyleId($styleId);
$hasStyleCustomBorders = ($associatedBorderId !== null && $associatedBorderId !== 0);
return ($hasStyleCustomFill || $hasStyleCustomBorders);
$associatedFormatId = $this->styleRegistry->getFormatIdForStyleId($styleId);
$hasStyleCustomFormats = ($associatedFormatId !== null && $associatedFormatId !== 0);
return ($hasStyleCustomFill || $hasStyleCustomBorders || $hasStyleCustomFormats);
}
/**
@ -48,6 +51,7 @@ class StyleManager extends \Box\Spout\Writer\Common\Manager\Style\StyleManager
<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
EOD;
$content .= $this->getFormatsSectionContent();
$content .= $this->getFontsSectionContent();
$content .= $this->getFillsSectionContent();
$content .= $this->getBordersSectionContent();
@ -62,6 +66,35 @@ EOD;
return $content;
}
/**
* Returns the content of the "<numFmts>" section.
*
* @return string
*/
protected function getFormatsSectionContent()
{
$tags = [];
$registeredFormats = $this->styleRegistry->getRegisteredFormats();
foreach ($registeredFormats as $styleId) {
$numFmtId = $this->styleRegistry->getFormatIdForStyleId($styleId);
//Built-in formats do not need to be declared, skip them
if ($numFmtId < 164) {
continue;
}
/** @var Style $style */
$style = $this->styleRegistry->getStyleFromStyleId($styleId);
$format = $style->getFormat();
$tags[] = '<numFmt numFmtId="' . $numFmtId . '" formatCode="' . $format . '"/>';
}
$content = '<numFmts count="' . \count($tags) . '">';
$content .= \implode('', $tags);
$content .= '</numFmts>';
return $content;
}
/**
* Returns the content of the "<fonts>" section.
*
@ -71,7 +104,7 @@ EOD;
{
$registeredStyles = $this->styleRegistry->getRegisteredStyles();
$content = '<fonts count="' . count($registeredStyles) . '">';
$content = '<fonts count="' . \count($registeredStyles) . '">';
/** @var Style $style */
foreach ($registeredStyles as $style) {
@ -112,8 +145,8 @@ EOD;
$registeredFills = $this->styleRegistry->getRegisteredFills();
// Excel reserves two default fills
$fillsCount = count($registeredFills) + 2;
$content = sprintf('<fills count="%d">', $fillsCount);
$fillsCount = \count($registeredFills) + 2;
$content = \sprintf('<fills count="%d">', $fillsCount);
$content .= '<fill><patternFill patternType="none"/></fill>';
$content .= '<fill><patternFill patternType="gray125"/></fill>';
@ -124,7 +157,7 @@ EOD;
$style = $this->styleRegistry->getStyleFromStyleId($styleId);
$backgroundColor = $style->getBackgroundColor();
$content .= sprintf(
$content .= \sprintf(
'<fill><patternFill patternType="solid"><fgColor rgb="%s"/></patternFill></fill>',
$backgroundColor
);
@ -145,7 +178,7 @@ EOD;
$registeredBorders = $this->styleRegistry->getRegisteredBorders();
// There is one default border with index 0
$borderCount = count($registeredBorders) + 1;
$borderCount = \count($registeredBorders) + 1;
$content = '<borders count="' . $borderCount . '">';
@ -200,24 +233,32 @@ EOD;
{
$registeredStyles = $this->styleRegistry->getRegisteredStyles();
$content = '<cellXfs count="' . count($registeredStyles) . '">';
$content = '<cellXfs count="' . \count($registeredStyles) . '">';
foreach ($registeredStyles as $style) {
$styleId = $style->getId();
$fillId = $this->getFillIdForStyleId($styleId);
$borderId = $this->getBorderIdForStyleId($styleId);
$numFmtId = $this->getFormatIdForStyleId($styleId);
$content .= '<xf numFmtId="0" fontId="' . $styleId . '" fillId="' . $fillId . '" borderId="' . $borderId . '" xfId="0"';
$content .= '<xf numFmtId="' . $numFmtId . '" fontId="' . $styleId . '" fillId="' . $fillId . '" borderId="' . $borderId . '" xfId="0"';
if ($style->shouldApplyFont()) {
$content .= ' applyFont="1"';
}
$content .= sprintf(' applyBorder="%d"', $style->shouldApplyBorder() ? 1 : 0);
$content .= \sprintf(' applyBorder="%d"', $style->shouldApplyBorder() ? 1 : 0);
if ($style->shouldWrapText()) {
if ($style->shouldApplyCellAlignment() || $style->shouldWrapText()) {
$content .= ' applyAlignment="1">';
$content .= '<alignment wrapText="1"/>';
$content .= '<alignment';
if ($style->shouldApplyCellAlignment()) {
$content .= \sprintf(' horizontal="%s"', $style->getCellAlignment());
}
if ($style->shouldWrapText()) {
$content .= ' wrapText="1"';
}
$content .= '/>';
$content .= '</xf>';
} else {
$content .= '/>';
@ -261,6 +302,22 @@ EOD;
return $isDefaultStyle ? 0 : ($this->styleRegistry->getBorderIdForStyleId($styleId) ?: 0);
}
/**
* Returns the format ID associated to the given style ID.
* For the default style use general format.
*
* @param int $styleId
* @return int
*/
private function getFormatIdForStyleId($styleId)
{
// For the default style (ID = 0), we don't want to override the format.
// Otherwise all cells of the spreadsheet will have a format.
$isDefaultStyle = ($styleId === 0);
return $isDefaultStyle ? 0 : ($this->styleRegistry->getFormatIdForStyleId($styleId) ?: 0);
}
/**
* Returns the content of the "<cellStyles>" section.
*

View file

@ -10,6 +10,79 @@ use Box\Spout\Common\Entity\Style\Style;
*/
class StyleRegistry extends \Box\Spout\Writer\Common\Manager\Style\StyleRegistry
{
/**
* @see https://msdn.microsoft.com/en-us/library/ff529597(v=office.12).aspx
* @var array Mapping between built-in format and the associated numFmtId
*/
protected static $builtinNumFormatToIdMapping = [
'General' => 0,
'0' => 1,
'0.00' => 2,
'#,##0' => 3,
'#,##0.00' => 4,
'$#,##0,\-$#,##0' => 5,
'$#,##0,[Red]\-$#,##0' => 6,
'$#,##0.00,\-$#,##0.00' => 7,
'$#,##0.00,[Red]\-$#,##0.00' => 8,
'0%' => 9,
'0.00%' => 10,
'0.00E+00' => 11,
'# ?/?' => 12,
'# ??/??' => 13,
'mm-dd-yy' => 14,
'd-mmm-yy' => 15,
'd-mmm' => 16,
'mmm-yy' => 17,
'h:mm AM/PM' => 18,
'h:mm:ss AM/PM' => 19,
'h:mm' => 20,
'h:mm:ss' => 21,
'm/d/yy h:mm' => 22,
'#,##0 ,(#,##0)' => 37,
'#,##0 ,[Red](#,##0)' => 38,
'#,##0.00,(#,##0.00)' => 39,
'#,##0.00,[Red](#,##0.00)' => 40,
'_("$"* #,##0.00_),_("$"* \(#,##0.00\),_("$"* "-"??_),_(@_)' => 44,
'mm:ss' => 45,
'[h]:mm:ss' => 46,
'mm:ss.0' => 47,
'##0.0E+0' => 48,
'@' => 49,
'[$-404]e/m/d' => 27,
'm/d/yy' => 30,
't0' => 59,
't0.00' => 60,
't#,##0' => 61,
't#,##0.00' => 62,
't0%' => 67,
't0.00%' => 68,
't# ?/?' => 69,
't# ??/??' => 70,
];
/**
* @var array
*/
protected $registeredFormats = [];
/**
* @var array [STYLE_ID] => [FORMAT_ID] maps a style to a format declaration
*/
protected $styleIdToFormatsMappingTable = [];
/**
* If the numFmtId is lower than 0xA4 (164 in decimal)
* then it's a built-in number format.
* Since Excel is the dominant vendor - we play along here
*
* @var int The fill index counter for custom fills.
*/
protected $formatIndex = 164;
/**
* @var array
*/
@ -48,11 +121,52 @@ class StyleRegistry extends \Box\Spout\Writer\Common\Manager\Style\StyleRegistry
{
$registeredStyle = parent::registerStyle($style);
$this->registerFill($registeredStyle);
$this->registerFormat($registeredStyle);
$this->registerBorder($registeredStyle);
return $registeredStyle;
}
/**
* Register a format definition
*
* @param Style $style
*/
protected function registerFormat(Style $style)
{
$styleId = $style->getId();
$format = $style->getFormat();
if ($format) {
$isFormatRegistered = isset($this->registeredFormats[$format]);
// We need to track the already registered format definitions
if ($isFormatRegistered) {
$registeredStyleId = $this->registeredFormats[$format];
$registeredFormatId = $this->styleIdToFormatsMappingTable[$registeredStyleId];
$this->styleIdToFormatsMappingTable[$styleId] = $registeredFormatId;
} else {
$this->registeredFormats[$format] = $styleId;
$id = self::$builtinNumFormatToIdMapping[$format] ?? $this->formatIndex++;
$this->styleIdToFormatsMappingTable[$styleId] = $id;
}
} else {
// The formatId maps a style to a format declaration
// When there is no format definition - we default to 0 ( General )
$this->styleIdToFormatsMappingTable[$styleId] = 0;
}
}
/**
* @param int $styleId
* @return int|null Format ID associated to the given style ID
*/
public function getFormatIdForStyleId($styleId)
{
return $this->styleIdToFormatsMappingTable[$styleId] ?? null;
}
/**
* Register a fill definition
*
@ -107,7 +221,7 @@ class StyleRegistry extends \Box\Spout\Writer\Common\Manager\Style\StyleRegistry
if ($style->shouldApplyBorder()) {
$border = $style->getBorder();
$serializedBorder = serialize($border);
$serializedBorder = \serialize($border);
$isBorderAlreadyRegistered = isset($this->registeredBorders[$serializedBorder]);
@ -117,7 +231,7 @@ class StyleRegistry extends \Box\Spout\Writer\Common\Manager\Style\StyleRegistry
$this->styleIdToBorderMappingTable[$styleId] = $registeredBorderId;
} else {
$this->registeredBorders[$serializedBorder] = $styleId;
$this->styleIdToBorderMappingTable[$styleId] = count($this->registeredBorders);
$this->styleIdToBorderMappingTable[$styleId] = \count($this->registeredBorders);
}
} else {
// If no border should be applied - the mapping is the default border: 0
@ -151,4 +265,12 @@ class StyleRegistry extends \Box\Spout\Writer\Common\Manager\Style\StyleRegistry
{
return $this->registeredBorders;
}
/**
* @return array
*/
public function getRegisteredFormats()
{
return $this->registeredFormats;
}
}

View file

@ -44,7 +44,7 @@ class WorkbookManager extends WorkbookManagerAbstract
{
$worksheetFilesFolder = $this->fileSystemHelper->getXlWorksheetsFolder();
return $worksheetFilesFolder . '/' . strtolower($sheet->getName()) . '.xml';
return $worksheetFilesFolder . '/' . \strtolower($sheet->getName()) . '.xml';
}
/**

View file

@ -107,13 +107,13 @@ EOD;
*/
public function startSheet(Worksheet $worksheet)
{
$sheetFilePointer = fopen($worksheet->getFilePath(), 'w');
$sheetFilePointer = \fopen($worksheet->getFilePath(), 'w');
$this->throwIfSheetFilePointerIsNotAvailable($sheetFilePointer);
$worksheet->setFilePointer($sheetFilePointer);
fwrite($sheetFilePointer, self::SHEET_XML_FILE_HEADER);
fwrite($sheetFilePointer, '<sheetData>');
\fwrite($sheetFilePointer, self::SHEET_XML_FILE_HEADER);
\fwrite($sheetFilePointer, '<sheetData>');
}
/**
@ -153,21 +153,19 @@ EOD;
*/
private function addNonEmptyRow(Worksheet $worksheet, Row $row)
{
$cellIndex = 0;
$rowStyle = $row->getStyle();
$rowIndex = $worksheet->getLastWrittenRowIndex() + 1;
$rowIndexOneBased = $worksheet->getLastWrittenRowIndex() + 1;
$numCells = $row->getNumCells();
$rowXML = '<row r="' . $rowIndex . '" spans="1:' . $numCells . '">';
$rowXML = '<row r="' . $rowIndexOneBased . '" spans="1:' . $numCells . '">';
foreach ($row->getCells() as $cell) {
$rowXML .= $this->applyStyleAndGetCellXML($cell, $rowStyle, $rowIndex, $cellIndex);
$cellIndex++;
foreach ($row->getCells() as $columnIndexZeroBased => $cell) {
$rowXML .= $this->applyStyleAndGetCellXML($cell, $rowStyle, $rowIndexOneBased, $columnIndexZeroBased);
}
$rowXML .= '</row>';
$wasWriteSuccessful = fwrite($worksheet->getFilePointer(), $rowXML);
$wasWriteSuccessful = \fwrite($worksheet->getFilePointer(), $rowXML);
if ($wasWriteSuccessful === false) {
throw new IOException("Unable to write data in {$worksheet->getFilePath()}");
}
@ -177,14 +175,15 @@ EOD;
* Applies styles to the given style, merging the cell's style with its row's style
* Then builds and returns xml for the cell.
*
* @param Cell $cell
* @param Cell $cell
* @param Style $rowStyle
* @param int $rowIndex
* @param int $cellIndex
* @param int $rowIndexOneBased
* @param int $columnIndexZeroBased
*
* @throws InvalidArgumentException If the given value cannot be processed
* @return string
*/
private function applyStyleAndGetCellXML(Cell $cell, Style $rowStyle, $rowIndex, $cellIndex)
private function applyStyleAndGetCellXML(Cell $cell, Style $rowStyle, $rowIndexOneBased, $columnIndexZeroBased)
{
// Apply row and extra styles
$mergedCellAndRowStyle = $this->styleMerger->merge($cell->getStyle(), $rowStyle);
@ -193,23 +192,24 @@ EOD;
$registeredStyle = $this->styleManager->registerStyle($newCellStyle);
return $this->getCellXML($rowIndex, $cellIndex, $cell, $registeredStyle->getId());
return $this->getCellXML($rowIndexOneBased, $columnIndexZeroBased, $cell, $registeredStyle->getId());
}
/**
* Builds and returns xml for a single cell.
*
* @param int $rowIndex
* @param int $cellNumber
* @param int $rowIndexOneBased
* @param int $columnIndexZeroBased
* @param Cell $cell
* @param int $styleId
* @param int $styleId
*
* @throws InvalidArgumentException If the given value cannot be processed
* @return string
*/
private function getCellXML($rowIndex, $cellNumber, Cell $cell, $styleId)
private function getCellXML($rowIndexOneBased, $columnIndexZeroBased, Cell $cell, $styleId)
{
$columnIndex = CellHelper::getCellIndexFromColumnIndex($cellNumber);
$cellXML = '<c r="' . $columnIndex . $rowIndex . '"';
$columnLetters = CellHelper::getColumnLettersFromColumnIndex($columnIndexZeroBased);
$cellXML = '<c r="' . $columnLetters . $rowIndexOneBased . '"';
$cellXML .= ' s="' . $styleId . '"';
if ($cell->isString()) {
@ -218,6 +218,9 @@ EOD;
$cellXML .= ' t="b"><v>' . (int) ($cell->getValue()) . '</v></c>';
} elseif ($cell->isNumeric()) {
$cellXML .= '><v>' . $cell->getValue() . '</v></c>';
} elseif ($cell->isError() && is_string($cell->getValueEvenIfError())) {
// only writes the error value if it's a string
$cellXML .= ' t="e"><v>' . $cell->getValueEvenIfError() . '</v></c>';
} elseif ($cell->isEmpty()) {
if ($this->styleManager->shouldApplyStyleOnEmptyCell($styleId)) {
$cellXML .= '/>';
@ -227,7 +230,7 @@ EOD;
$cellXML = '';
}
} else {
throw new InvalidArgumentException('Trying to add a value with an unsupported type: ' . gettype($cell->getValue()));
throw new InvalidArgumentException('Trying to add a value with an unsupported type: ' . \gettype($cell->getValue()));
}
return $cellXML;
@ -263,12 +266,12 @@ EOD;
{
$worksheetFilePointer = $worksheet->getFilePointer();
if (!is_resource($worksheetFilePointer)) {
if (!\is_resource($worksheetFilePointer)) {
return;
}
fwrite($worksheetFilePointer, '</sheetData>');
fwrite($worksheetFilePointer, '</worksheet>');
fclose($worksheetFilePointer);
\fwrite($worksheetFilePointer, '</sheetData>');
\fwrite($worksheetFilePointer, '</worksheet>');
\fclose($worksheetFilePointer);
}
}