Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d4cf853270 | ||
|
|
8bac924d48 | ||
|
|
591d86cf07 | ||
|
|
ba4a55292d | ||
|
|
d617a50b85 | ||
|
|
92981fa80b | ||
|
|
766c733466 | ||
|
|
46cae76546 | ||
|
|
03be58b84d | ||
|
|
b21bb86682 | ||
|
|
8a45cec220 | ||
|
|
04d41d7c9f | ||
|
|
f8f194bc8f | ||
|
|
044ad11596 | ||
|
|
935ba1fb5e | ||
|
|
fb0175d633 | ||
|
|
69190cd855 | ||
|
|
13a53fb43f | ||
|
|
a848be52de | ||
|
|
52faef0168 | ||
|
|
3b4dfba38e | ||
|
|
cfd3e0ffa3 |
No files matched your search
@@ -1,11 +1,13 @@
|
|||||||
# Spout
|
# Spout
|
||||||
|
|
||||||
|
[](https://packagist.org/packages/box/spout)
|
||||||
|
[](http://opensource.box.com/badges)
|
||||||
|
[](http://travis-ci.org/box/spout)
|
||||||
|
[](https://packagist.org/packages/box/spout) [](https://packagist.org/packages/box/spout)
|
||||||
|
|
||||||
Spout is a PHP library to read and write CSV and XLSX files, in a fast and scalable way.
|
Spout is a PHP library to read and write CSV and XLSX files, 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 10MB).
|
Contrary to other file readers or writers, it is capable of processing very large files while keeping the memory usage really low (less than 10MB).
|
||||||
|
|
||||||
[](http://travis-ci.org/box/spout)
|
|
||||||
[](http://opensource.box.com/badges)
|
|
||||||
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
@@ -248,6 +250,11 @@ Here are a few numbers regarding the performance of Spout:
|
|||||||
| Read XLSX (using shared strings) | 1 second | 1-2 minutes | 35-40 minutes |
|
| Read XLSX (using shared strings) | 1 second | 1-2 minutes | 35-40 minutes |
|
||||||
| Write XLSX | 1 second | 20-25 seconds | 8-10 minutes |
|
| Write XLSX | 1 second | 20-25 seconds | 8-10 minutes |
|
||||||
|
|
||||||
|
#### Does Spout support charts or formulas?
|
||||||
|
|
||||||
|
No. This is a compromise to keep memory usage low. Charts and formulas requires data to be kept in memory in order to be used.
|
||||||
|
So the larger the file would be, the more memory would be consumed, preventing your code to scale well.
|
||||||
|
|
||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
"name": "box/spout",
|
"name": "box/spout",
|
||||||
"description": "PHP Library to read and write CSV and XLSX files, in a fast and scalable way",
|
"description": "PHP Library to read and write CSV and XLSX files, in a fast and scalable way",
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"version": "1.0.6",
|
|
||||||
"keywords": ["php","read","write","csv","xlsx","excel","spreadsheet","scale","memory","stream","ooxml"],
|
"keywords": ["php","read","write","csv","xlsx","excel","spreadsheet","scale","memory","stream","ooxml"],
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"homepage": "https://www.github.com/box/spout",
|
"homepage": "https://www.github.com/box/spout",
|
||||||
|
|||||||
@@ -91,6 +91,21 @@ class GlobalFunctionsHelper
|
|||||||
return fgetcsv($handle, $length, $delimiter, $enclosure);
|
return fgetcsv($handle, $length, $delimiter, $enclosure);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper around global function fputcsv()
|
||||||
|
* @see fputcsv()
|
||||||
|
*
|
||||||
|
* @param resource $handle
|
||||||
|
* @param array $fields
|
||||||
|
* @param string|void $delimiter
|
||||||
|
* @param string|void $enclosure
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function fputcsv($handle, array $fields, $delimiter = null, $enclosure = null)
|
||||||
|
{
|
||||||
|
return fputcsv($handle, $fields, $delimiter, $enclosure);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper around global function fclose()
|
* Wrapper around global function fclose()
|
||||||
* @see fclose()
|
* @see fclose()
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ use Box\Spout\Reader\Exception\EndOfFileReachedException;
|
|||||||
*/
|
*/
|
||||||
abstract class AbstractReader implements ReaderInterface
|
abstract class AbstractReader implements ReaderInterface
|
||||||
{
|
{
|
||||||
/** @var int Used to keep track of the row number */
|
/** @var int Used to keep track of the row index */
|
||||||
protected $currentRowIndex = 0;
|
protected $currentRowIndex = 0;
|
||||||
|
|
||||||
/** @var bool Indicates whether the stream is currently open */
|
/** @var bool Indicates whether the stream is currently open */
|
||||||
|
|||||||
@@ -71,6 +71,24 @@ class SharedStringsHelper
|
|||||||
$this->tempFolder = $this->fileSystemHelper->createFolder($rootTempFolder, uniqid('sharedstrings'));
|
$this->tempFolder = $this->fileSystemHelper->createFolder($rootTempFolder, uniqid('sharedstrings'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the XLSX file contains a shared strings XML file
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function hasSharedStrings()
|
||||||
|
{
|
||||||
|
$hasSharedStrings = false;
|
||||||
|
$zip = new \ZipArchive();
|
||||||
|
|
||||||
|
if ($zip->open($this->filePath) === true) {
|
||||||
|
$hasSharedStrings = ($zip->locateName(self::SHARED_STRINGS_XML_FILE_PATH) !== false);
|
||||||
|
$zip->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $hasSharedStrings;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds an in-memory array containing all the shared strings of the worksheet.
|
* Builds an in-memory array containing all the shared strings of the worksheet.
|
||||||
* All the strings are stored in a XML file, located at 'xl/sharedStrings.xml'.
|
* All the strings are stored in a XML file, located at 'xl/sharedStrings.xml'.
|
||||||
@@ -93,7 +111,7 @@ class SharedStringsHelper
|
|||||||
$this->tempFilePointer = null;
|
$this->tempFilePointer = null;
|
||||||
$escaper = new \Box\Spout\Common\Escaper\XLSX();
|
$escaper = new \Box\Spout\Common\Escaper\XLSX();
|
||||||
|
|
||||||
$sharedStringsFilePath = 'zip://' . $this->filePath . '#' . self::SHARED_STRINGS_XML_FILE_PATH;
|
$sharedStringsFilePath = $this->getSharedStringsFilePath();
|
||||||
if ($xmlReader->open($sharedStringsFilePath, null, LIBXML_NONET) === false) {
|
if ($xmlReader->open($sharedStringsFilePath, null, LIBXML_NONET) === false) {
|
||||||
throw new IOException('Could not open "' . self::SHARED_STRINGS_XML_FILE_PATH . '".');
|
throw new IOException('Could not open "' . self::SHARED_STRINGS_XML_FILE_PATH . '".');
|
||||||
}
|
}
|
||||||
@@ -143,6 +161,15 @@ class SharedStringsHelper
|
|||||||
$xmlReader->close();
|
$xmlReader->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string The path to the shared strings XML file, working cross-platforms
|
||||||
|
*/
|
||||||
|
protected function getSharedStringsFilePath()
|
||||||
|
{
|
||||||
|
$sharedStringsXmlFilePath = str_replace('/', DIRECTORY_SEPARATOR, self::SHARED_STRINGS_XML_FILE_PATH);
|
||||||
|
return 'zip://' . $this->filePath . '#' . $sharedStringsXmlFilePath;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes nodes that should not be read, like the pronunciation of the Kanji characters.
|
* Removes nodes that should not be read, like the pronunciation of the Kanji characters.
|
||||||
* By keeping them, their text content would be added to the read string.
|
* By keeping them, their text content would be added to the read string.
|
||||||
|
|||||||
@@ -91,12 +91,12 @@ class WorksheetHelper
|
|||||||
* default to the data sheet XML file name ("xl/worksheets/sheet2.xml" => "sheet2").
|
* default to the data sheet XML file name ("xl/worksheets/sheet2.xml" => "sheet2").
|
||||||
*
|
*
|
||||||
* @param string $sheetDataXMLFilePath Path of the sheet data XML file as in [Content_Types].xml
|
* @param string $sheetDataXMLFilePath Path of the sheet data XML file as in [Content_Types].xml
|
||||||
* @param int $sheetNumberZeroBased Index of the sheet, based on order in [Content_Types].xml (zero-based)
|
* @param int $sheetIndexZeroBased Index of the sheet, based on order in [Content_Types].xml (zero-based)
|
||||||
* @return \Box\Spout\Reader\Sheet Sheet instance
|
* @return \Box\Spout\Reader\Sheet Sheet instance
|
||||||
*/
|
*/
|
||||||
protected function getSheet($sheetDataXMLFilePath, $sheetNumberZeroBased)
|
protected function getSheet($sheetDataXMLFilePath, $sheetIndexZeroBased)
|
||||||
{
|
{
|
||||||
$sheetId = $sheetNumberZeroBased + 1;
|
$sheetId = $sheetIndexZeroBased + 1;
|
||||||
$sheetName = $this->getDefaultSheetName($sheetDataXMLFilePath);
|
$sheetName = $this->getDefaultSheetName($sheetDataXMLFilePath);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -126,7 +126,7 @@ class WorksheetHelper
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Sheet($sheetId, $sheetNumberZeroBased, $sheetName);
|
return new Sheet($sheetId, $sheetIndexZeroBased, $sheetName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -186,7 +186,8 @@ class WorksheetHelper
|
|||||||
*/
|
*/
|
||||||
protected function getFileAsXMLElementWithNamespace($xmlFilePath, $mainNamespace)
|
protected function getFileAsXMLElementWithNamespace($xmlFilePath, $mainNamespace)
|
||||||
{
|
{
|
||||||
$xmlContents = $this->globalFunctionsHelper->file_get_contents('zip://' . $this->filePath . '#' . $xmlFilePath);
|
$normalizedXmlFilePath = str_replace('/', DIRECTORY_SEPARATOR, $xmlFilePath);
|
||||||
|
$xmlContents = $this->globalFunctionsHelper->file_get_contents('zip://' . $this->filePath . '#' . $normalizedXmlFilePath);
|
||||||
|
|
||||||
$xmlElement = new \SimpleXMLElement($xmlContents);
|
$xmlElement = new \SimpleXMLElement($xmlContents);
|
||||||
$xmlElement->registerXPathNamespace('ns', $mainNamespace);
|
$xmlElement->registerXPathNamespace('ns', $mainNamespace);
|
||||||
@@ -204,6 +205,6 @@ class WorksheetHelper
|
|||||||
*/
|
*/
|
||||||
public function hasNextWorksheet($currentWorksheet, $allWorksheets)
|
public function hasNextWorksheet($currentWorksheet, $allWorksheets)
|
||||||
{
|
{
|
||||||
return ($currentWorksheet === null || ($currentWorksheet->getWorksheetNumber() + 1 < count($allWorksheets)));
|
return ($currentWorksheet === null || ($currentWorksheet->getWorksheetIndex() + 1 < count($allWorksheets)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -13,30 +13,32 @@ class Worksheet
|
|||||||
/** @var \Box\Spout\Reader\Sheet The "external" sheet */
|
/** @var \Box\Spout\Reader\Sheet The "external" sheet */
|
||||||
protected $externalSheet;
|
protected $externalSheet;
|
||||||
|
|
||||||
/** @var int Worksheet number, based on the order of appareance in [Content_Types].xml (zero-based) */
|
/** @var int Worksheet index, based on the order of appareance in [Content_Types].xml (zero-based) */
|
||||||
protected $worksheetNumber;
|
protected $worksheetIndex;
|
||||||
|
|
||||||
/** @var string Path of the XML file containing the worksheet data */
|
/** @var string Path of the XML file containing the worksheet data */
|
||||||
protected $dataXmlFilePath;
|
protected $dataXmlFilePath;
|
||||||
|
|
||||||
/**\
|
/**\
|
||||||
* @param \Box\Spout\Reader\Sheet $externalSheet The associated "external" sheet
|
* @param \Box\Spout\Reader\Sheet $externalSheet The associated "external" sheet
|
||||||
* @param int $worksheetNumber Worksheet number, based on the order of appareance in [Content_Types].xml (zero-based)
|
* @param int $worksheetIndex Worksheet index, based on the order of appareance in [Content_Types].xml (zero-based)
|
||||||
* @param string $dataXmlFilePath Path of the XML file containing the worksheet data
|
* @param string $dataXmlFilePath Path of the XML file containing the worksheet data
|
||||||
*/
|
*/
|
||||||
public function __construct($externalSheet, $worksheetNumber, $dataXmlFilePath)
|
public function __construct($externalSheet, $worksheetIndex, $dataXmlFilePath)
|
||||||
{
|
{
|
||||||
$this->externalSheet = $externalSheet;
|
$this->externalSheet = $externalSheet;
|
||||||
$this->worksheetNumber = $worksheetNumber;
|
$this->worksheetIndex = $worksheetIndex;
|
||||||
$this->dataXmlFilePath = $dataXmlFilePath;
|
$this->dataXmlFilePath = $dataXmlFilePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string Path of the XML file containing the worksheet data, without the leading slash
|
* @return string Path of the XML file containing the worksheet data,
|
||||||
|
* without the leading slash and working cross-platforms.
|
||||||
*/
|
*/
|
||||||
public function getDataXmlFilePath()
|
public function getDataXmlFilePath()
|
||||||
{
|
{
|
||||||
return ltrim($this->dataXmlFilePath, '/');
|
$dataXmlFilePath = ltrim($this->dataXmlFilePath, '/');
|
||||||
|
return str_replace('/', DIRECTORY_SEPARATOR, $dataXmlFilePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -50,8 +52,8 @@ class Worksheet
|
|||||||
/**
|
/**
|
||||||
* @return int
|
* @return int
|
||||||
*/
|
*/
|
||||||
public function getWorksheetNumber()
|
public function getWorksheetIndex()
|
||||||
{
|
{
|
||||||
return $this->worksheetNumber;
|
return $this->worksheetIndex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -13,21 +13,21 @@ class Sheet
|
|||||||
/** @var int ID of the sheet */
|
/** @var int ID of the sheet */
|
||||||
protected $id;
|
protected $id;
|
||||||
|
|
||||||
/** @var int Number of the sheet, based on order of creation (zero-based) */
|
/** @var int Index of the sheet, based on order of creation (zero-based) */
|
||||||
protected $number;
|
protected $index;
|
||||||
|
|
||||||
/** @var string Name of the sheet */
|
/** @var string Name of the sheet */
|
||||||
protected $name;
|
protected $name;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param int $sheetId ID of the sheet
|
* @param int $sheetId ID of the sheet
|
||||||
* @param int $sheetNumber Number of the sheet, based on order of creation (zero-based)
|
* @param int $sheetIndex Index of the sheet, based on order of creation (zero-based)
|
||||||
* @param string $sheetName Name of the sheet
|
* @param string $sheetName Name of the sheet
|
||||||
*/
|
*/
|
||||||
function __construct($sheetId, $sheetNumber, $sheetName)
|
function __construct($sheetId, $sheetIndex, $sheetName)
|
||||||
{
|
{
|
||||||
$this->id = $sheetId;
|
$this->id = $sheetId;
|
||||||
$this->number = $sheetNumber;
|
$this->index = $sheetIndex;
|
||||||
$this->name = $sheetName;
|
$this->name = $sheetName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,11 +40,11 @@ class Sheet
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return int Number of the sheet, based on order of creation (zero-based)
|
* @return int Index of the sheet, based on order of creation (zero-based)
|
||||||
*/
|
*/
|
||||||
public function getNumber()
|
public function getIndex()
|
||||||
{
|
{
|
||||||
return $this->number;
|
return $this->index;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+66
-26
@@ -72,8 +72,12 @@ class XLSX extends AbstractReader
|
|||||||
$this->zip = new \ZipArchive();
|
$this->zip = new \ZipArchive();
|
||||||
|
|
||||||
if ($this->zip->open($filePath) === true) {
|
if ($this->zip->open($filePath) === true) {
|
||||||
// Extracts all the strings from the worksheets for easy access in the future
|
$this->sharedStringsHelper = new SharedStringsHelper($filePath, $this->tempFolder);
|
||||||
$this->extractSharedStrings($filePath);
|
|
||||||
|
if ($this->sharedStringsHelper->hasSharedStrings()) {
|
||||||
|
// Extracts all the strings from the worksheets for easy access in the future
|
||||||
|
$this->sharedStringsHelper->extractSharedStrings();
|
||||||
|
}
|
||||||
|
|
||||||
// Fetch all available worksheets
|
// Fetch all available worksheets
|
||||||
$this->worksheetHelper = new WorksheetHelper($filePath, $this->globalFunctionsHelper);
|
$this->worksheetHelper = new WorksheetHelper($filePath, $this->globalFunctionsHelper);
|
||||||
@@ -87,19 +91,6 @@ class XLSX extends AbstractReader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Builds an in-memory array containing all the shared strings of the worksheets.
|
|
||||||
*
|
|
||||||
* @param string $filePath Path of the XLSX file to be read
|
|
||||||
* @return void
|
|
||||||
* @throws \Box\Spout\Common\Exception\IOException If sharedStrings XML file can't be read
|
|
||||||
*/
|
|
||||||
protected function extractSharedStrings($filePath)
|
|
||||||
{
|
|
||||||
$this->sharedStringsHelper = new SharedStringsHelper($filePath, $this->tempFolder);
|
|
||||||
$this->sharedStringsHelper->extractSharedStrings();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether another worksheet exists after the current worksheet.
|
* Returns whether another worksheet exists after the current worksheet.
|
||||||
*
|
*
|
||||||
@@ -132,8 +123,8 @@ class XLSX extends AbstractReader
|
|||||||
if ($this->currentWorksheet === null) {
|
if ($this->currentWorksheet === null) {
|
||||||
$nextWorksheet = $this->worksheets[0];
|
$nextWorksheet = $this->worksheets[0];
|
||||||
} else {
|
} else {
|
||||||
$currentWorksheetNumber = $this->currentWorksheet->getWorksheetNumber();
|
$currentWorksheetIndex = $this->currentWorksheet->getWorksheetIndex();
|
||||||
$nextWorksheet = $this->worksheets[$currentWorksheetNumber + 1];
|
$nextWorksheet = $this->worksheets[$currentWorksheetIndex + 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->initXmlReaderForWorksheetData($nextWorksheet);
|
$this->initXmlReaderForWorksheetData($nextWorksheet);
|
||||||
@@ -186,6 +177,7 @@ class XLSX extends AbstractReader
|
|||||||
throw new BadUsageException('You must call nextSheet() before calling hasNextRow() or nextRow()');
|
throw new BadUsageException('You must call nextSheet() before calling hasNextRow() or nextRow()');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$escaper = new \Box\Spout\Common\Escaper\XLSX();
|
||||||
$isInsideRowTag = false;
|
$isInsideRowTag = false;
|
||||||
$rowData = [];
|
$rowData = [];
|
||||||
|
|
||||||
@@ -197,6 +189,7 @@ class XLSX extends AbstractReader
|
|||||||
$lastCellIndex = $matches[1];
|
$lastCellIndex = $matches[1];
|
||||||
$this->numberOfColumns = CellHelper::getColumnIndexFromCellIndex($lastCellIndex) + 1;
|
$this->numberOfColumns = CellHelper::getColumnIndexFromCellIndex($lastCellIndex) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if ($this->xmlReader->nodeType == \XMLReader::ELEMENT && $this->xmlReader->name === 'row') {
|
} else if ($this->xmlReader->nodeType == \XMLReader::ELEMENT && $this->xmlReader->name === 'row') {
|
||||||
// Start of the row description
|
// Start of the row description
|
||||||
$isInsideRowTag = true;
|
$isInsideRowTag = true;
|
||||||
@@ -209,20 +202,15 @@ class XLSX extends AbstractReader
|
|||||||
$numberOfColumnsForRow = intval($numberOfColumnsForRow);
|
$numberOfColumnsForRow = intval($numberOfColumnsForRow);
|
||||||
}
|
}
|
||||||
$rowData = ($numberOfColumnsForRow !== 0) ? array_fill(0, $numberOfColumnsForRow, '') : [];
|
$rowData = ($numberOfColumnsForRow !== 0) ? array_fill(0, $numberOfColumnsForRow, '') : [];
|
||||||
|
|
||||||
} else if ($isInsideRowTag && $this->xmlReader->nodeType == \XMLReader::ELEMENT && $this->xmlReader->name === 'c') {
|
} else if ($isInsideRowTag && $this->xmlReader->nodeType == \XMLReader::ELEMENT && $this->xmlReader->name === 'c') {
|
||||||
// Start of a cell description
|
// Start of a cell description
|
||||||
$currentCellIndex = $this->xmlReader->getAttribute('r');
|
$currentCellIndex = $this->xmlReader->getAttribute('r');
|
||||||
$currentColumnIndex = CellHelper::getColumnIndexFromCellIndex($currentCellIndex);
|
$currentColumnIndex = CellHelper::getColumnIndexFromCellIndex($currentCellIndex);
|
||||||
$node = $this->xmlReader->expand();
|
|
||||||
|
|
||||||
$hasSharedString = ($this->xmlReader->getAttribute('t') === 's');
|
$node = $this->xmlReader->expand();
|
||||||
if ($hasSharedString) {
|
$rowData[$currentColumnIndex] = $this->getCellValue($node, $escaper);
|
||||||
$sharedStringIndex = intval($node->nodeValue);
|
|
||||||
$rowData[$currentColumnIndex] = $this->sharedStringsHelper->getStringAtIndex($sharedStringIndex);
|
|
||||||
} else {
|
|
||||||
// for inline strings or numbers, just get the value
|
|
||||||
$rowData[$currentColumnIndex] = trim($node->nodeValue);
|
|
||||||
}
|
|
||||||
} else if ($this->xmlReader->nodeType == \XMLReader::END_ELEMENT && $this->xmlReader->name === 'row') {
|
} else if ($this->xmlReader->nodeType == \XMLReader::END_ELEMENT && $this->xmlReader->name === 'row') {
|
||||||
// End of the row description
|
// End of the row description
|
||||||
// If needed, we fill the empty cells
|
// If needed, we fill the empty cells
|
||||||
@@ -235,6 +223,58 @@ class XLSX extends AbstractReader
|
|||||||
return ($rowData !== []) ? $rowData : null;
|
return ($rowData !== []) ? $rowData : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the (unescaped) cell value associated to the given XML node.
|
||||||
|
*
|
||||||
|
* @param \DOMNode $node
|
||||||
|
* @param \Box\Spout\Common\Escaper\XLSX $escaper
|
||||||
|
* @return string|int|float|bool|null The value associated with the cell (null when the cell has an error)
|
||||||
|
*/
|
||||||
|
protected function getCellValue($node, $escaper)
|
||||||
|
{
|
||||||
|
$cellValue = '';
|
||||||
|
|
||||||
|
// Default cell type is "n"
|
||||||
|
$cellType = $node->getAttribute('t') ?: 'n';
|
||||||
|
|
||||||
|
if ($cellType === 'inlineStr') {
|
||||||
|
// inline strings are formatted this way:
|
||||||
|
// <c r="A1" t="inlineStr"><is><t>[INLINE_STRING]</t></is></c>
|
||||||
|
$tNode = $node->getElementsByTagName('t')->item(0);
|
||||||
|
$escapedCellValue = trim($tNode->nodeValue);
|
||||||
|
$cellValue = $escaper->unescape($escapedCellValue);
|
||||||
|
} else {
|
||||||
|
// all other cell types should have a "v" tag containing the value.
|
||||||
|
// if not, the returned value should be empty string.
|
||||||
|
$vNode = $node->getElementsByTagName('v')->item(0);
|
||||||
|
|
||||||
|
if ($vNode !== null) {
|
||||||
|
if ($cellType === 's') {
|
||||||
|
// shared strings are formatted this way:
|
||||||
|
// <c r="A1" t="s"><v>[SHARED_STRING_INDEX]</v></c>
|
||||||
|
$sharedStringIndex = intval($vNode->nodeValue);
|
||||||
|
$escapedCellValue = $this->sharedStringsHelper->getStringAtIndex($sharedStringIndex);
|
||||||
|
$cellValue = $escaper->unescape($escapedCellValue);
|
||||||
|
} else if ($cellType === 'b') {
|
||||||
|
// !! is similar to boolval()
|
||||||
|
$cellValue = !!$vNode->nodeValue;
|
||||||
|
} else if ($cellType === 'n') {
|
||||||
|
$nodeValue = $vNode->nodeValue;
|
||||||
|
$cellValue = is_int($nodeValue) ? intval($nodeValue) : floatval($nodeValue);
|
||||||
|
} else if ($cellType === 'd') {
|
||||||
|
$cellValue = new \DateTime($vNode->nodeValue);
|
||||||
|
} else if ($cellType === 'e') {
|
||||||
|
$cellValue = null;
|
||||||
|
} else if ($cellType === 'str') {
|
||||||
|
$escapedCellValue = trim($vNode->nodeValue);
|
||||||
|
$cellValue = $escaper->unescape($escapedCellValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $cellValue;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes the reader. To be used after reading the file.
|
* Closes the reader. To be used after reading the file.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ class CSV extends AbstractWriter
|
|||||||
*/
|
*/
|
||||||
protected function addRowToWriter(array $dataRow)
|
protected function addRowToWriter(array $dataRow)
|
||||||
{
|
{
|
||||||
$wasWriteSuccessful = fputcsv($this->filePointer, $dataRow, $this->fieldDelimiter, $this->fieldEnclosure);
|
$wasWriteSuccessful = $this->globalFunctionsHelper->fputcsv($this->filePointer, $dataRow, $this->fieldDelimiter, $this->fieldEnclosure);
|
||||||
if ($wasWriteSuccessful === false) {
|
if ($wasWriteSuccessful === false) {
|
||||||
throw new IOException('Unable to write data');
|
throw new IOException('Unable to write data');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,8 +67,8 @@ class Workbook
|
|||||||
*/
|
*/
|
||||||
public function addNewSheet()
|
public function addNewSheet()
|
||||||
{
|
{
|
||||||
$newSheetNumber = count($this->worksheets);
|
$newSheetIndex = count($this->worksheets);
|
||||||
$sheet = new Sheet($newSheetNumber);
|
$sheet = new Sheet($newSheetIndex);
|
||||||
|
|
||||||
$worksheetFilesFolder = $this->fileSystemHelper->getXlWorksheetsFolder();
|
$worksheetFilesFolder = $this->fileSystemHelper->getXlWorksheetsFolder();
|
||||||
$worksheet = new Worksheet($sheet, $worksheetFilesFolder, $this->sharedStringsHelper, $this->shouldUseInlineStrings);
|
$worksheet = new Worksheet($sheet, $worksheetFilesFolder, $this->sharedStringsHelper, $this->shouldUseInlineStrings);
|
||||||
|
|||||||
@@ -96,8 +96,8 @@ EOD;
|
|||||||
*/
|
*/
|
||||||
public function getId()
|
public function getId()
|
||||||
{
|
{
|
||||||
// sheet number is zero-based, while ID is 1-based
|
// sheet index is zero-based, while ID is 1-based
|
||||||
return $this->externalSheet->getNumber() + 1;
|
return $this->externalSheet->getIndex() + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -12,27 +12,27 @@ class Sheet
|
|||||||
{
|
{
|
||||||
const DEFAULT_SHEET_NAME_PREFIX = 'Sheet';
|
const DEFAULT_SHEET_NAME_PREFIX = 'Sheet';
|
||||||
|
|
||||||
/** @var int Number of the sheet, based on order of creation (zero-based) */
|
/** @var int Index of the sheet, based on order of creation (zero-based) */
|
||||||
protected $sheetNumber;
|
protected $index;
|
||||||
|
|
||||||
/** @var string Name of the sheet */
|
/** @var string Name of the sheet */
|
||||||
protected $name;
|
protected $name;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $sheetNumber Number of the sheet, based on order of creation (zero-based)
|
* @param int $sheetIndex Index of the sheet, based on order of creation (zero-based)
|
||||||
*/
|
*/
|
||||||
function __construct($sheetNumber)
|
function __construct($sheetIndex)
|
||||||
{
|
{
|
||||||
$this->sheetNumber = $sheetNumber;
|
$this->index = $sheetIndex;
|
||||||
$this->name = self::DEFAULT_SHEET_NAME_PREFIX . ($sheetNumber + 1);
|
$this->name = self::DEFAULT_SHEET_NAME_PREFIX . ($sheetIndex + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return int Number of the sheet, based on order of creation (zero-based)
|
* @return int Index of the sheet, based on order of creation (zero-based)
|
||||||
*/
|
*/
|
||||||
public function getNumber()
|
public function getIndex()
|
||||||
{
|
{
|
||||||
return $this->sheetNumber;
|
return $this->index;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Box\Spout\Reader;
|
||||||
|
|
||||||
|
use Box\Spout\Common\Type;
|
||||||
|
use Box\Spout\TestUsingResource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class SheetTest
|
||||||
|
*
|
||||||
|
* @package Box\Spout\Reader
|
||||||
|
*/
|
||||||
|
class SheetTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
use TestUsingResource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testNextSheetShouldReturnCorrectSheetInfos()
|
||||||
|
{
|
||||||
|
$sheets = $this->openFileAndReturnSheets('two_sheets_with_custom_names.xlsx');
|
||||||
|
|
||||||
|
$this->assertEquals('CustomName1', $sheets[0]->getName());
|
||||||
|
$this->assertEquals(0, $sheets[0]->getIndex());
|
||||||
|
$this->assertEquals(1, $sheets[0]->getId());
|
||||||
|
|
||||||
|
$this->assertEquals('CustomName2', $sheets[1]->getName());
|
||||||
|
$this->assertEquals(1, $sheets[1]->getIndex());
|
||||||
|
$this->assertEquals(2, $sheets[1]->getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $fileName
|
||||||
|
* @return Sheet[]
|
||||||
|
*/
|
||||||
|
private function openFileAndReturnSheets($fileName)
|
||||||
|
{
|
||||||
|
$resourcePath = $this->getResourcePath($fileName);
|
||||||
|
$reader = ReaderFactory::create(Type::XLSX);
|
||||||
|
$reader->open($resourcePath);
|
||||||
|
|
||||||
|
$sheets = [];
|
||||||
|
while ($reader->hasNextSheet()) {
|
||||||
|
$sheets[] = $reader->nextSheet();
|
||||||
|
}
|
||||||
|
|
||||||
|
$reader->close();
|
||||||
|
|
||||||
|
return $sheets;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -38,6 +38,37 @@ class XLSXTest extends \PHPUnit_Framework_TestCase
|
|||||||
$this->getAllRowsForFile($filePath);
|
$this->getAllRowsForFile($filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \Box\Spout\Reader\Exception\ReaderNotOpenedException
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testHasNextSheetShouldThrowExceptionIfReaderNotOpened()
|
||||||
|
{
|
||||||
|
$reader = ReaderFactory::create(Type::XLSX);
|
||||||
|
$reader->hasNextSheet();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \Box\Spout\Reader\Exception\EndOfWorksheetsReachedException
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testNextSheetShouldThrowExceptionIfNoMoreSheetsToRead()
|
||||||
|
{
|
||||||
|
$fileName = 'one_sheet_with_shared_strings.xlsx';
|
||||||
|
$resourcePath = $this->getResourcePath($fileName);
|
||||||
|
|
||||||
|
$reader = ReaderFactory::create(Type::XLSX);
|
||||||
|
$reader->open($resourcePath);
|
||||||
|
|
||||||
|
while ($reader->hasNextSheet()) {
|
||||||
|
$reader->nextSheet();
|
||||||
|
}
|
||||||
|
|
||||||
|
$reader->nextSheet();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
@@ -69,6 +100,41 @@ class XLSXTest extends \PHPUnit_Framework_TestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testReadShouldSupportFilesWithoutSharedStringsFile()
|
||||||
|
{
|
||||||
|
$allRows = $this->getAllRowsForFile('sheet_with_no_shared_strings_file.xlsx');
|
||||||
|
|
||||||
|
$expectedRows = [
|
||||||
|
[10, 11],
|
||||||
|
[20, 21],
|
||||||
|
];
|
||||||
|
$this->assertEquals($expectedRows, $allRows);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testReadShouldSupportAllCellTypes()
|
||||||
|
{
|
||||||
|
$allRows = $this->getAllRowsForFile('sheet_with_all_cell_types.xlsx');
|
||||||
|
|
||||||
|
$expectedRows = [
|
||||||
|
[
|
||||||
|
's1--A1', 's1--A2',
|
||||||
|
false, true,
|
||||||
|
\DateTime::createFromFormat('Y-m-d H:i:s', '2015-06-03 13:21:58'),
|
||||||
|
\DateTime::createFromFormat('Y-m-d H:i:s', '2015-06-01 00:00:00'),
|
||||||
|
10, 10.43,
|
||||||
|
null,
|
||||||
|
],
|
||||||
|
['', '', '', '', '', '', '', '', ''],
|
||||||
|
];
|
||||||
|
$this->assertEquals($expectedRows, $allRows);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
@@ -203,27 +269,16 @@ class XLSXTest extends \PHPUnit_Framework_TestCase
|
|||||||
/**
|
/**
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function testNextSheetShouldReturnCorrectSheetInfos()
|
public function testReadShouldSkipFormulas()
|
||||||
{
|
{
|
||||||
$resourcePath = $this->getResourcePath('two_sheets_with_custom_names.xlsx');
|
$allRows = $this->getAllRowsForFile('sheet_with_formulas.xlsx');
|
||||||
$reader = ReaderFactory::create(Type::XLSX);
|
|
||||||
$reader->open($resourcePath);
|
|
||||||
|
|
||||||
/** @var \Box\Spout\Reader\Sheet[] $sheets */
|
$expectedRows = [
|
||||||
$sheets = [];
|
['val1', 'val2', 'total1', 'total2'],
|
||||||
while ($reader->hasNextSheet()) {
|
[10, 20, 30, 21],
|
||||||
$sheets[] = $reader->nextSheet();
|
[11, 21, 32, 41],
|
||||||
}
|
];
|
||||||
|
$this->assertEquals($expectedRows, $allRows);
|
||||||
$reader->close();
|
|
||||||
|
|
||||||
$this->assertEquals('CustomName1', $sheets[0]->getName());
|
|
||||||
$this->assertEquals(0, $sheets[0]->getNumber());
|
|
||||||
$this->assertEquals(1, $sheets[0]->getId());
|
|
||||||
|
|
||||||
$this->assertEquals('CustomName2', $sheets[1]->getName());
|
|
||||||
$this->assertEquals(1, $sheets[1]->getNumber());
|
|
||||||
$this->assertEquals(2, $sheets[1]->getId());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -17,13 +17,13 @@ class SheetTest extends \PHPUnit_Framework_TestCase
|
|||||||
/**
|
/**
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function testGetSheetNumber()
|
public function testGetSheetIndex()
|
||||||
{
|
{
|
||||||
$sheets = $this->writeDataAndReturnSheets('test_get_sheet_number.xlsx');
|
$sheets = $this->writeDataAndReturnSheets('test_get_sheet_index.xlsx');
|
||||||
|
|
||||||
$this->assertEquals(2, count($sheets), '2 sheets should have been created');
|
$this->assertEquals(2, count($sheets), '2 sheets should have been created');
|
||||||
$this->assertEquals(0, $sheets[0]->getNumber(), 'The first sheet should be number 0');
|
$this->assertEquals(0, $sheets[0]->getIndex(), 'The first sheet should be index 0');
|
||||||
$this->assertEquals(1, $sheets[1]->getNumber(), 'The second sheet should be number 1');
|
$this->assertEquals(1, $sheets[1]->getIndex(), 'The second sheet should be index 1');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -38,6 +38,28 @@ class SheetTest extends \PHPUnit_Framework_TestCase
|
|||||||
$this->assertEquals('Sheet2', $sheets[1]->getName(), 'Invalid name for the second sheet');
|
$this->assertEquals('Sheet2', $sheets[1]->getName(), 'Invalid name for the second sheet');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testSetSheetNameShouldCreateSheetWithCustomName()
|
||||||
|
{
|
||||||
|
$fileName = 'test_set_name_should_create_sheet_with_custom_name.xlsx';
|
||||||
|
$this->createGeneratedFolderIfNeeded($fileName);
|
||||||
|
$resourcePath = $this->getGeneratedResourcePath($fileName);
|
||||||
|
|
||||||
|
$writer = WriterFactory::create(Type::XLSX);
|
||||||
|
$writer->openToFile($resourcePath);
|
||||||
|
|
||||||
|
$customSheetName = 'CustomName';
|
||||||
|
$sheet = $writer->getCurrentSheet();
|
||||||
|
$sheet->setName($customSheetName);
|
||||||
|
|
||||||
|
$writer->addRow(['xlsx--11', 'xlsx--12']);
|
||||||
|
$writer->close();
|
||||||
|
|
||||||
|
$this->assertSheetNameEquals($customSheetName, $resourcePath, "The sheet name should have been changed to '$customSheetName'");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $fileName
|
* @param string $fileName
|
||||||
* @return Sheet[]
|
* @return Sheet[]
|
||||||
@@ -59,4 +81,27 @@ class SheetTest extends \PHPUnit_Framework_TestCase
|
|||||||
|
|
||||||
return $writer->getSheets();
|
return $writer->getSheets();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $expectedName
|
||||||
|
* @param string $resourcePath
|
||||||
|
* @param string $message
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function assertSheetNameEquals($expectedName, $resourcePath, $message = '')
|
||||||
|
{
|
||||||
|
$pathToWorkbookFile = $resourcePath . $this->normalizePath('#xl/workbook.xml');
|
||||||
|
$xmlContents = file_get_contents('zip://' . $pathToWorkbookFile);
|
||||||
|
|
||||||
|
$this->assertContains('<sheet name="' . $expectedName . '"', $xmlContents, $message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $path
|
||||||
|
* @return string The path with the correct directory separators, as defined for the current platform
|
||||||
|
*/
|
||||||
|
private function normalizePath($path)
|
||||||
|
{
|
||||||
|
return str_replace('/', DIRECTORY_SEPARATOR, $path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -343,28 +343,6 @@ class XLSXTest extends \PHPUnit_Framework_TestCase
|
|||||||
$this->assertInlineDataWasWrittenToSheet($fileName, 1, 'control's _x0015_ "character"');
|
$this->assertInlineDataWasWrittenToSheet($fileName, 1, 'control's _x0015_ "character"');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function testSetNameShouldCreateSheetWithCustomName()
|
|
||||||
{
|
|
||||||
$fileName = 'test_set_name_should_create_sheet_with_custom_name.xlsx';
|
|
||||||
$this->createGeneratedFolderIfNeeded($fileName);
|
|
||||||
$resourcePath = $this->getGeneratedResourcePath($fileName);
|
|
||||||
|
|
||||||
$writer = WriterFactory::create(Type::XLSX);
|
|
||||||
$writer->openToFile($resourcePath);
|
|
||||||
|
|
||||||
$customSheetName = 'CustomName';
|
|
||||||
$sheet = $writer->getCurrentSheet();
|
|
||||||
$sheet->setName($customSheetName);
|
|
||||||
|
|
||||||
$writer->addRow(['xlsx--11', 'xlsx--12']);
|
|
||||||
$writer->close();
|
|
||||||
|
|
||||||
$this->assertSheetNameEquals($customSheetName, $resourcePath, "The sheet name should have been changed to '$customSheetName'");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array $allRows
|
* @param array $allRows
|
||||||
* @param string $fileName
|
* @param string $fileName
|
||||||
@@ -422,15 +400,15 @@ class XLSXTest extends \PHPUnit_Framework_TestCase
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $fileName
|
* @param string $fileName
|
||||||
* @param int $sheetNumber
|
* @param int $sheetIndex
|
||||||
* @param mixed $inlineData
|
* @param mixed $inlineData
|
||||||
* @param string $message
|
* @param string $message
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
private function assertInlineDataWasWrittenToSheet($fileName, $sheetNumber, $inlineData, $message = '')
|
private function assertInlineDataWasWrittenToSheet($fileName, $sheetIndex, $inlineData, $message = '')
|
||||||
{
|
{
|
||||||
$resourcePath = $this->getGeneratedResourcePath($fileName);
|
$resourcePath = $this->getGeneratedResourcePath($fileName);
|
||||||
$pathToSheetFile = $resourcePath . '#xl/worksheets/sheet' . $sheetNumber . '.xml';
|
$pathToSheetFile = $resourcePath . $this->normalizePath('#xl/worksheets/sheet' . $sheetIndex . '.xml');
|
||||||
$xmlContents = file_get_contents('zip://' . $pathToSheetFile);
|
$xmlContents = file_get_contents('zip://' . $pathToSheetFile);
|
||||||
|
|
||||||
$this->assertContains((string)$inlineData, $xmlContents, $message);
|
$this->assertContains((string)$inlineData, $xmlContents, $message);
|
||||||
@@ -438,15 +416,15 @@ class XLSXTest extends \PHPUnit_Framework_TestCase
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $fileName
|
* @param string $fileName
|
||||||
* @param int $sheetNumber
|
* @param int $sheetIndex
|
||||||
* @param mixed $inlineData
|
* @param mixed $inlineData
|
||||||
* @param string $message
|
* @param string $message
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
private function assertInlineDataWasNotWrittenToSheet($fileName, $sheetNumber, $inlineData, $message = '')
|
private function assertInlineDataWasNotWrittenToSheet($fileName, $sheetIndex, $inlineData, $message = '')
|
||||||
{
|
{
|
||||||
$resourcePath = $this->getGeneratedResourcePath($fileName);
|
$resourcePath = $this->getGeneratedResourcePath($fileName);
|
||||||
$pathToSheetFile = $resourcePath . '#xl/worksheets/sheet' . $sheetNumber . '.xml';
|
$pathToSheetFile = $resourcePath . $this->normalizePath('#xl/worksheets/sheet' . $sheetIndex . '.xml');
|
||||||
$xmlContents = file_get_contents('zip://' . $pathToSheetFile);
|
$xmlContents = file_get_contents('zip://' . $pathToSheetFile);
|
||||||
|
|
||||||
$this->assertNotContains((string)$inlineData, $xmlContents, $message);
|
$this->assertNotContains((string)$inlineData, $xmlContents, $message);
|
||||||
@@ -461,23 +439,18 @@ class XLSXTest extends \PHPUnit_Framework_TestCase
|
|||||||
private function assertSharedStringWasWritten($fileName, $sharedString, $message = '')
|
private function assertSharedStringWasWritten($fileName, $sharedString, $message = '')
|
||||||
{
|
{
|
||||||
$resourcePath = $this->getGeneratedResourcePath($fileName);
|
$resourcePath = $this->getGeneratedResourcePath($fileName);
|
||||||
$pathToSharedStringsFile = $resourcePath . '#xl/sharedStrings.xml';
|
$pathToSharedStringsFile = $resourcePath . $this->normalizePath('#xl/sharedStrings.xml');
|
||||||
$xmlContents = file_get_contents('zip://' . $pathToSharedStringsFile);
|
$xmlContents = file_get_contents('zip://' . $pathToSharedStringsFile);
|
||||||
|
|
||||||
$this->assertContains($sharedString, $xmlContents, $message);
|
$this->assertContains($sharedString, $xmlContents, $message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $expectedName
|
* @param string $path
|
||||||
* @param string $resourcePath
|
* @return string The path with the correct directory separators, as defined for the current platform
|
||||||
* @param string $message
|
|
||||||
* @return void
|
|
||||||
*/
|
*/
|
||||||
private function assertSheetNameEquals($expectedName, $resourcePath, $message = '')
|
private function normalizePath($path)
|
||||||
{
|
{
|
||||||
$pathToWorkbookFile = $resourcePath . '#xl/workbook.xml';
|
return str_replace('/', DIRECTORY_SEPARATOR, $path);
|
||||||
$xmlContents = file_get_contents('zip://' . $pathToWorkbookFile);
|
|
||||||
|
|
||||||
$this->assertContains('<sheet name="' . $expectedName . '"', $xmlContents, $message);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in new issue
Block a user