Skip to content

Commit

Permalink
Merge branch 'MDL-71707' of https://github.com/paulholden/moodle
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewnicols committed Oct 6, 2021
2 parents d26bd7c + 968b872 commit 7c357c2
Show file tree
Hide file tree
Showing 26 changed files with 338 additions and 97 deletions.
12 changes: 6 additions & 6 deletions lib/spout/README.md
@@ -1,26 +1,26 @@
# Spout

[![Latest Stable Version](https://poser.pugx.org/box/spout/v/stable)](https://packagist.org/packages/box/spout)
[![Project Status](http://opensource.box.com/badges/active.svg)](http://opensource.box.com/badges)
[![Project Status](https://opensource.box.com/badges/active.svg)](https://opensource.box.com/badges)
[![Build Status](https://travis-ci.org/box/spout.svg?branch=master)](https://travis-ci.org/box/spout)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/box/spout/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/box/spout/?branch=master)
[![Code Coverage](https://scrutinizer-ci.com/g/box/spout/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/box/spout/?branch=master)
[![Total Downloads](https://poser.pugx.org/box/spout/downloads)](https://packagist.org/packages/box/spout)

Spout is a PHP library to read and write spreadsheet files (CSV, XLSX and ODS), in a fast and scalable way.
Contrary to other file readers or writers, it is capable of processing very large files while keeping the memory usage really low (less than 3MB).
Unlike other file readers or writers, it is capable of processing very large files, while keeping the memory usage really low (less than 3MB).

Join the community and come discuss about Spout: [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/box/spout?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
Join the community and come discuss Spout: [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/box/spout?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)


## Documentation

Full documentation can be found at [http://opensource.box.com/spout/](http://opensource.box.com/spout/).
Full documentation can be found at [https://opensource.box.com/spout/](https://opensource.box.com/spout/).


## Requirements

* PHP version 7.1 or higher
* PHP version 7.2 or higher
* PHP extension `php_zip` enabled
* PHP extension `php_xmlreader` enabled

Expand All @@ -43,7 +43,7 @@ For information, the performance tests take about 10 minutes to run (processing

## Support

You can ask questions, submit new features ideas or discuss about Spout in the chat room:<br>
You can ask questions, submit new features ideas or discuss Spout in the chat room:<br>
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/box/spout?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)

## Copyright and License
Expand Down
5 changes: 5 additions & 0 deletions lib/spout/readme_moodle.txt
Expand Up @@ -4,6 +4,11 @@ Description of Spout library import
* Only include the src/Spout directory.
* Update lib/thirdpartylibs.xml with the latest version.

2021/09/01
----------
Update to v3.3.0 (MDL-71707)
by Paul Holden <paulh@moodle.com>

2020/12/07
----------
Update to v3.1.0 (MDL-70302)
Expand Down
2 changes: 1 addition & 1 deletion lib/spout/src/Spout/Common/Entity/Cell.php
Expand Up @@ -65,7 +65,7 @@ class Cell
protected $style;

/**
* @param $value mixed
* @param mixed|null $value
* @param Style|null $style
*/
public function __construct($value, Style $style = null)
Expand Down
43 changes: 43 additions & 0 deletions lib/spout/src/Spout/Common/Entity/Style/Style.php
Expand Up @@ -84,6 +84,12 @@ class Style
/** @var bool */
private $hasSetFormat = false;

/** @var bool */
private $isRegistered = false;

/** @var bool */
private $isEmpty = true;

/**
* @return int|null
*/
Expand Down Expand Up @@ -119,6 +125,7 @@ public function setBorder(Border $border)
{
$this->shouldApplyBorder = true;
$this->border = $border;
$this->isEmpty = false;

return $this;
}
Expand Down Expand Up @@ -147,6 +154,7 @@ public function setFontBold()
$this->fontBold = true;
$this->hasSetFontBold = true;
$this->shouldApplyFont = true;
$this->isEmpty = false;

return $this;
}
Expand Down Expand Up @@ -175,6 +183,7 @@ public function setFontItalic()
$this->fontItalic = true;
$this->hasSetFontItalic = true;
$this->shouldApplyFont = true;
$this->isEmpty = false;

return $this;
}
Expand Down Expand Up @@ -203,6 +212,7 @@ public function setFontUnderline()
$this->fontUnderline = true;
$this->hasSetFontUnderline = true;
$this->shouldApplyFont = true;
$this->isEmpty = false;

return $this;
}
Expand Down Expand Up @@ -231,6 +241,7 @@ public function setFontStrikethrough()
$this->fontStrikethrough = true;
$this->hasSetFontStrikethrough = true;
$this->shouldApplyFont = true;
$this->isEmpty = false;

return $this;
}
Expand Down Expand Up @@ -260,6 +271,7 @@ public function setFontSize($fontSize)
$this->fontSize = $fontSize;
$this->hasSetFontSize = true;
$this->shouldApplyFont = true;
$this->isEmpty = false;

return $this;
}
Expand Down Expand Up @@ -291,6 +303,7 @@ public function setFontColor($fontColor)
$this->fontColor = $fontColor;
$this->hasSetFontColor = true;
$this->shouldApplyFont = true;
$this->isEmpty = false;

return $this;
}
Expand Down Expand Up @@ -320,6 +333,7 @@ public function setFontName($fontName)
$this->fontName = $fontName;
$this->hasSetFontName = true;
$this->shouldApplyFont = true;
$this->isEmpty = false;

return $this;
}
Expand Down Expand Up @@ -350,6 +364,7 @@ public function setCellAlignment($cellAlignment)
$this->cellAlignment = $cellAlignment;
$this->hasSetCellAlignment = true;
$this->shouldApplyCellAlignment = true;
$this->isEmpty = false;

return $this;
}
Expand Down Expand Up @@ -386,6 +401,7 @@ public function setShouldWrapText($shouldWrap = true)
{
$this->shouldWrapText = $shouldWrap;
$this->hasSetWrapText = true;
$this->isEmpty = false;

return $this;
}
Expand Down Expand Up @@ -415,6 +431,7 @@ public function setBackgroundColor($color)
{
$this->hasSetBackgroundColor = true;
$this->backgroundColor = $color;
$this->isEmpty = false;

return $this;
}
Expand Down Expand Up @@ -444,6 +461,7 @@ public function setFormat($format)
{
$this->hasSetFormat = true;
$this->format = $format;
$this->isEmpty = false;

return $this;
}
Expand All @@ -463,4 +481,29 @@ public function shouldApplyFormat()
{
return $this->hasSetFormat;
}

/**
* @return bool
*/
public function isRegistered() : bool
{
return $this->isRegistered;
}

public function markAsRegistered(?int $id) : void
{
$this->setId($id);
$this->isRegistered = true;
}

public function unmarkAsRegistered() : void
{
$this->setId(0);
$this->isRegistered = false;
}

public function isEmpty() : bool
{
return $this->isEmpty;
}
}
10 changes: 7 additions & 3 deletions lib/spout/src/Spout/Common/Helper/FileSystemHelper.php
Expand Up @@ -17,7 +17,7 @@ class FileSystemHelper implements FileSystemHelperInterface
/**
* @param string $baseFolderPath The path of the base folder where all the I/O can occur
*/
public function __construct($baseFolderPath)
public function __construct(string $baseFolderPath)
{
$this->baseFolderRealPath = \realpath($baseFolderPath);
}
Expand Down Expand Up @@ -117,12 +117,16 @@ public function deleteFolderRecursively($folderPath)
* should occur is not inside the base folder.
*
* @param string $operationFolderPath The path of the folder where the I/O operation should occur
* @throws \Box\Spout\Common\Exception\IOException If the folder where the I/O operation should occur is not inside the base folder
* @throws \Box\Spout\Common\Exception\IOException If the folder where the I/O operation should occur
* is not inside the base folder or the base folder does not exist
* @return void
*/
protected function throwIfOperationNotInBaseFolder($operationFolderPath)
protected function throwIfOperationNotInBaseFolder(string $operationFolderPath)
{
$operationFolderRealPath = \realpath($operationFolderPath);
if (!$this->baseFolderRealPath) {
throw new IOException("The base folder path is invalid: {$this->baseFolderRealPath}");
}
$isInBaseFolder = (\strpos($operationFolderRealPath, $this->baseFolderRealPath) === 0);
if (!$isInBaseFolder) {
throw new IOException("Cannot perform I/O operation outside of the base folder: {$this->baseFolderRealPath}");
Expand Down
34 changes: 34 additions & 0 deletions lib/spout/src/Spout/Common/Helper/StringHelper.php
Expand Up @@ -13,12 +13,20 @@ class StringHelper
/** @var bool Whether the mbstring extension is loaded */
protected $hasMbstringSupport;

/** @var bool Whether the code is running with PHP7 or older versions */
private $isRunningPhp7OrOlder;

/** @var array Locale info, used for number formatting */
private $localeInfo;

/**
*
*/
public function __construct()
{
$this->hasMbstringSupport = \extension_loaded('mbstring');
$this->isRunningPhp7OrOlder = \version_compare(PHP_VERSION, '8.0.0') < 0;
$this->localeInfo = \localeconv();
}

/**
Expand Down Expand Up @@ -68,4 +76,30 @@ public function getCharLastOccurrencePosition($char, $string)

return ($position !== false) ? $position : -1;
}

/**
* Formats a numeric value (int or float) in a way that's compatible with the expected spreadsheet format.
*
* Formatting of float values is locale dependent in PHP < 8.
* Thousands separators and decimal points vary from locale to locale (en_US: 12.34 vs pl_PL: 12,34).
* However, float values must be formatted with no thousands separator and a "." as decimal point
* to work properly. This method can be used to convert the value to the correct format before storing it.
*
* @see https://wiki.php.net/rfc/locale_independent_float_to_string for the changed behavior in PHP8.
*
* @param int|float $numericValue
* @return string
*/
public function formatNumericValue($numericValue)
{
if ($this->isRunningPhp7OrOlder && is_float($numericValue)) {
return str_replace(
[$this->localeInfo['thousands_sep'], $this->localeInfo['decimal_point']],
['', '.'],
$numericValue
);
}

return $numericValue;
}
}
13 changes: 13 additions & 0 deletions lib/spout/src/Spout/Reader/Common/Manager/RowManager.php
Expand Up @@ -56,12 +56,25 @@ public function fillMissingIndexesWithEmptyCells(Row $row)
$rowCells = $row->getCells();
$maxCellIndex = $numCells;

// If the row has empty cells, calling "setCellAtIndex" will add the cell
// but in the wrong place (the new cell is added at the end of the array).
// Therefore, we need to sort the array using keys to have proper order.
// @see https://github.com/box/spout/issues/740
$needsSorting = false;

for ($cellIndex = 0; $cellIndex < $maxCellIndex; $cellIndex++) {
if (!isset($rowCells[$cellIndex])) {
$row->setCellAtIndex($this->entityFactory->createCell(''), $cellIndex);
$needsSorting = true;
}
}

if ($needsSorting) {
$rowCells = $row->getCells();
ksort($rowCells);
$row->setCells($rowCells);
}

return $row;
}
}
4 changes: 2 additions & 2 deletions lib/spout/src/Spout/Reader/ODS/SheetIterator.php
Expand Up @@ -28,13 +28,13 @@ class SheetIterator implements IteratorInterface
const XML_ATTRIBUTE_TABLE_STYLE_NAME = 'table:style-name';
const XML_ATTRIBUTE_TABLE_DISPLAY = 'table:display';

/** @var string $filePath Path of the file to be read */
/** @var string Path of the file to be read */
protected $filePath;

/** @var \Box\Spout\Common\Manager\OptionsManagerInterface Reader's options manager */
protected $optionsManager;

/** @var InternalEntityFactory $entityFactory Factory to create entities */
/** @var InternalEntityFactory Factory to create entities */
protected $entityFactory;

/** @var XMLReader The XMLReader object that will help read sheet's XML data */
Expand Down
21 changes: 9 additions & 12 deletions lib/spout/src/Spout/Reader/XLSX/Helper/CellValueFormatter.php
Expand Up @@ -31,14 +31,6 @@ class CellValueFormatter

/** Constants used for date formatting */
const NUM_SECONDS_IN_ONE_DAY = 86400;
const NUM_SECONDS_IN_ONE_HOUR = 3600;
const NUM_SECONDS_IN_ONE_MINUTE = 60;

/**
* February 29th, 1900 is NOT a leap year but Excel thinks it is...
* @see https://en.wikipedia.org/wiki/Year_1900_problem#Microsoft_Excel
*/
const ERRONEOUS_EXCEL_LEAP_YEAR_DAY = 60;

/** @var SharedStringsManager Manages shared strings */
protected $sharedStringsManager;
Expand Down Expand Up @@ -130,10 +122,15 @@ protected function getVNodeValue($node)
*/
protected function formatInlineStringCellValue($node)
{
// inline strings are formatted this way:
// <c r="A1" t="inlineStr"><is><t>[INLINE_STRING]</t></is></c>
$tNode = $node->getElementsByTagName(self::XML_NODE_INLINE_STRING_VALUE)->item(0);
$cellValue = $this->escaper->unescape($tNode->nodeValue);
// inline strings are formatted this way (they can contain any number of <t> nodes):
// <c r="A1" t="inlineStr"><is><t>[INLINE_STRING]</t><t>[INLINE_STRING_2]</t></is></c>
$tNodes = $node->getElementsByTagName(self::XML_NODE_INLINE_STRING_VALUE);

$cellValue = '';
for ($i = 0; $i < $tNodes->count(); $i++) {
$tNode = $tNodes->item($i);
$cellValue .= $this->escaper->unescape($tNode->nodeValue);
}

return $cellValue;
}
Expand Down
Expand Up @@ -16,9 +16,6 @@
*/
class SharedStringsManager
{
/** Main namespace for the sharedStrings.xml file */
const MAIN_NAMESPACE_FOR_SHARED_STRINGS_XML = 'http://schemas.openxmlformats.org/spreadsheetml/2006/main';

/** Definition of XML nodes names used to parse data */
const XML_NODE_SST = 'sst';
const XML_NODE_SI = 'si';
Expand All @@ -43,7 +40,7 @@ class SharedStringsManager
/** @var InternalEntityFactory Factory to create entities */
protected $entityFactory;

/** @var HelperFactory $helperFactory Factory to create helpers */
/** @var HelperFactory Factory to create helpers */
protected $helperFactory;

/** @var CachingStrategyFactory Factory to create shared strings caching strategies */
Expand Down

0 comments on commit 7c357c2

Please sign in to comment.