mirror of
https://github.com/moodle/moodle.git
synced 2025-08-06 09:26:35 +02:00
MDL-70302 core: Upgrade spout to 3.1.0
This commit is contained in:
parent
fee3970787
commit
23df19225a
68 changed files with 787 additions and 311 deletions
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -91,6 +91,14 @@ class Cell
|
|||
return !$this->isError() ? $this->value : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getValueEvenIfError()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Style|null $style
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
32
lib/spout/src/Spout/Common/Entity/Style/CellAlignment.php
Normal file
32
lib/spout/src/Spout/Common/Entity/Style/CellAlignment.php
Normal 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]);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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';
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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}");
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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])) {
|
||||
|
|
|
@ -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 [
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -190,7 +190,7 @@ class SharedStringsManager
|
|||
$textNodeValue = $textNode->nodeValue;
|
||||
$shouldPreserveWhitespace = $this->shouldPreserveWhitespace($textNode);
|
||||
|
||||
$sharedStringValue .= ($shouldPreserveWhitespace) ? $textNodeValue : trim($textNodeValue);
|
||||
$sharedStringValue .= ($shouldPreserveWhitespace) ? $textNodeValue : \trim($textNodeValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ class Workbook
|
|||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->internalId = uniqid();
|
||||
$this->internalId = \uniqid();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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())));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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())));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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())));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ class WorkbookManager extends WorkbookManagerAbstract
|
|||
{
|
||||
$worksheetFilesFolder = $this->fileSystemHelper->getXlWorksheetsFolder();
|
||||
|
||||
return $worksheetFilesFolder . '/' . strtolower($sheet->getName()) . '.xml';
|
||||
return $worksheetFilesFolder . '/' . \strtolower($sheet->getName()) . '.xml';
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue