Compare commits

...
30 Commits
Author SHA1 Message Date
Adrien Loison e58284d27b Merge pull request #25 from box/bump_version_to_1.0.5
Bump version to 1.0.5
2015-04-16 15:22:08 -07:00
Adrien Loison 27a47f509f Bump version to 1.0.5 2015-04-16 15:05:41 -07:00
Adrien Loison 8aeb883ebc Merge pull request #23 from box/add_hhvm_and_nightly_in_travis
Add HHVM and PHP nighly in Travis
2015-04-16 15:04:07 -07:00
Adrien Loison 05c504e72c Add HHVM and PHP nighly in Travis 2015-04-16 14:56:07 -07:00
Adrien Loison abbc158877 Merge pull request #24 from box/add_improve_test_coverage
Add and improve test coverage
2015-04-16 14:55:42 -07:00
Adrien Loison 3f3461b002 Add and improve test coverage 2015-04-16 14:51:48 -07:00
Adrien Loison 2074781852 Merge pull request #22 from box/bump_version_to_1.0.4
Bump version to 1.0.4
2015-04-16 13:17:24 -07:00
Adrien Loison fd5cfc5440 Bump version to 1.0.4 2015-04-16 13:13:05 -07:00
Adrien Loison a538813827 Merge pull request #21 from box/fix_empty_shared_string_bug
Fix empty shared string bug
2015-04-16 13:08:56 -07:00
Adrien Loison 3e5ef284a5 Fix empty shared string bug
Replaced !$sharedString by $sharedString === null to avoid the case
when $sharedString = ''
2015-04-16 13:00:02 -07:00
Adrien Loison f770f38967 Merge pull request #19 from box/bump_version_to_1.0.3
Bump version to 1.0.3
2015-04-14 20:20:34 -07:00
Adrien Loison 0edd9c434f Merge pull request #20 from box/add_editorconfig
Add .editorconfig
2015-04-14 20:20:21 -07:00
Adrien Loison 3c9bf12a5f Add .editorconfig 2015-04-14 20:16:41 -07:00
Adrien Loison 69c1436625 Bump version to 1.0.3 2015-04-14 20:01:24 -07:00
Adrien Loison af940069bc Merge pull request #18 from box/better_guess_cell_type
Better guess the cell type based on its value
2015-04-14 19:58:38 -07:00
Adrien Loison d6155a4243 Better guess the cell type based on its value 2015-04-14 19:52:56 -07:00
Adrien Loison 538f6109ad Merge pull request #15 from box/add_test_for_skipping_empty_rows
Add test for skipping empty rows
2015-04-03 22:49:40 -07:00
Adrien Loison 93cdd398dd Add test for skipping empty rows 2015-04-03 22:45:09 -07:00
Adrien Loison 66e5792a7e Merge pull request #14 from box/bump_version_to_1.0.2
Bump version to 1.0.2
2015-04-03 22:29:18 -07:00
Adrien Loison d4dbc32a53 Bump version to 1.0.2 2015-04-03 22:28:49 -07:00
Adrien Loison 09eac62c22 Merge pull request #13 from box/random_fixes
Random fixes
2015-04-03 22:24:56 -07:00
Adrien Loison 7bb5e02d2c Fix Typo in CSV Reader 2015-04-03 22:09:25 -07:00
Adrien Loison aebdd1acd7 Empty $dataRow should not create an empty rw 2015-04-03 22:08:55 -07:00
Adrien Loison 41a449f245 Merge pull request #12 from box/fix_dir_separator_bug_on_windows
Fix DIRECTORY_SEPARATOR bug occurring on Windows
2015-04-03 21:17:49 -07:00
Adrien Loison 39c72a91b4 Fix DIRECTORY_SEPARATOR bug occurring on Windows
Fixes #3:
The sheetN.xml files location is dynamically retrieved when parsing
[Content_Types].xml. In this file, the location is like "/xl/worksheets/sheet1.xml".

Because the zip stream wrapper expects the zip entry name to be like
"xl/worksheets/sheet1/xml" (without the first "/"), this first "/"
needs to be trimmed. It was trimmed using:
  ltrim($this->dataXmlFilePath, DIRECTORY_SEPARATOR);
which obvisously does not work on Windows platform where DIRECTORY_SEPARATOR is "\".
Replacing DIRECTORY_SEPARATOR by '/' solves this issue.
2015-04-03 21:10:30 -07:00
Adrien Loison 4ca1fc5851 Merge pull request #8 from box/add_multiline_strings_support
Add support for multiline strings
2015-03-30 22:21:47 -07:00
Adrien Loison 419544a85f Update README.md 2015-03-27 17:05:17 -07:00
Adrien Loison 6e11a043c1 Add support for multiline strings
Escaped line feed characters in shared strings before processing them.
This makes every string remain on one single line and therefore allow
fast retrieval
Replaced usages of "\n" by PHP_EOL
Added test for multiline strings
2015-03-27 16:54:56 -07:00
Adrien Loison c24cdbb9be Merge pull request #7 from box/document_utf8_use
Document the use of UTF-8 by default
2015-03-26 15:36:17 -07:00
Adrien Loison 18f6e64910 Document the use of UTF-8 by default 2015-03-26 15:13:47 -07:00
28 changed files with 425 additions and 53 deletions

No files matched your search

+15
View File
@@ -0,0 +1,15 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
insert_final_newline = true
[{.travis.yml}]
indent_size = 2
[*.md]
trim_trailing_whitespace = false
+1
View File
@@ -1,4 +1,5 @@
/tests/resources/generated /tests/resources/generated
/tests/coverage
/vendor /vendor
composer.lock composer.lock
/.idea /.idea
+2
View File
@@ -4,6 +4,8 @@ php:
- 5.4 - 5.4
- 5.5 - 5.5
- 5.6 - 5.6
- nightly
- hhvm
install: install:
- composer self-update - composer self-update
+1 -1
View File
@@ -149,7 +149,7 @@ $writer->setShouldCreateNewSheetsAutomatically(true); // default value
$writer->setShouldCreateNewSheetsAutomatically(false); // will stop writing new data when limit is reached $writer->setShouldCreateNewSheetsAutomatically(false); // will stop writing new data when limit is reached
``` ```
### Using custom temporary folder #### Using custom temporary folder
Processing XLSX files require temporary files to be created. By default, Spout will use the system default temporary folder (as returned by sys_get_temp_dir()). It is possible to override this by explicitly setting it on the reader or writer: Processing XLSX files require temporary files to be created. By default, Spout will use the system default temporary folder (as returned by sys_get_temp_dir()). It is possible to override this by explicitly setting it on the reader or writer:
```php ```php
+1 -1
View File
@@ -2,7 +2,7 @@
"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.1", "version": "1.0.5",
"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",
+10
View File
@@ -14,4 +14,14 @@
</testsuite> </testsuite>
</testsuites> </testsuites>
<logging>
<log type="coverage-html" target="tests/coverage/"/>
</logging>
<filter>
<whitelist>
<directory suffix=".php">src/</directory>
</whitelist>
</filter>
</phpunit> </phpunit>
+4
View File
@@ -13,6 +13,8 @@ class CSV implements EscaperInterface
/** /**
* Escapes the given string to make it compatible with CSV * Escapes the given string to make it compatible with CSV
* *
* @codeCoverageIgnore
*
* @param string $string The string to escape * @param string $string The string to escape
* @return string The escaped string * @return string The escaped string
*/ */
@@ -24,6 +26,8 @@ class CSV implements EscaperInterface
/** /**
* Unescapes the given string to make it compatible with CSV * Unescapes the given string to make it compatible with CSV
* *
* @codeCoverageIgnore
*
* @param string $string The string to unescape * @param string $string The string to unescape
* @return string The unescaped string * @return string The unescaped string
*/ */
+1 -1
View File
@@ -27,7 +27,7 @@ class XLSX implements EscaperInterface
public function escape($string) public function escape($string)
{ {
$escapedString = $this->escapeControlCharacters($string); $escapedString = $this->escapeControlCharacters($string);
$escapedString = htmlspecialchars($escapedString, ENT_QUOTES, 'utf-8'); $escapedString = htmlspecialchars($escapedString, ENT_QUOTES);
return $escapedString; return $escapedString;
} }
@@ -6,6 +6,8 @@ namespace Box\Spout\Common\Helper;
* Class GlobalFunctionsHelper * Class GlobalFunctionsHelper
* This class wraps global functions to facilitate testing * This class wraps global functions to facilitate testing
* *
* @codeCoverageIgnore
*
* @package Box\Spout\Common\Helper * @package Box\Spout\Common\Helper
*/ */
class GlobalFunctionsHelper class GlobalFunctionsHelper
+1 -1
View File
@@ -52,7 +52,7 @@ class CSV extends AbstractReader
* The file must be UTF-8 encoded. * The file must be UTF-8 encoded.
* @TODO add encoding detection/conversion * @TODO add encoding detection/conversion
* *
* @param string $filePath Path of the XLSX file to be read * @param string $filePath Path of the CSV file to be read
* @return void * @return void
* @throws \Box\Spout\Common\Exception\IOException * @throws \Box\Spout\Common\Exception\IOException
*/ */
+1 -1
View File
@@ -25,7 +25,7 @@ class CellHelper
{ {
$existingIndexes = array_keys($dataArray); $existingIndexes = array_keys($dataArray);
$newIndexes = array_fill_keys(range(min($existingIndexes), max($existingIndexes)), $fillValue); $newIndexes = array_fill_keys(range(0, max($existingIndexes)), $fillValue);
$dataArray += $newIndexes; $dataArray += $newIndexes;
ksort($dataArray); ksort($dataArray);
@@ -31,6 +31,9 @@ class SharedStringsHelper
*/ */
const MAX_NUM_STRINGS_PER_TEMP_FILE = 10000; const MAX_NUM_STRINGS_PER_TEMP_FILE = 10000;
/** Value to use to escape the line feed character ("\n") */
const ESCAPED_LINE_FEED_CHARACTER = '_x000A_';
/** @var string Path of the XLSX file being read */ /** @var string Path of the XLSX file being read */
protected $filePath; protected $filePath;
@@ -80,7 +83,6 @@ class SharedStringsHelper
* Please note that SimpleXML does not provide such a functionality but since it is faster * Please note that SimpleXML does not provide such a functionality but since it is faster
* and more handy to parse few XML nodes, it is used in combination with XMLReader for that purpose. * and more handy to parse few XML nodes, it is used in combination with XMLReader for that purpose.
* *
* @param string $filePath
* @return void * @return void
* @throws \Box\Spout\Common\Exception\IOException If sharedStrings.xml can't be read * @throws \Box\Spout\Common\Exception\IOException If sharedStrings.xml can't be read
*/ */
@@ -120,7 +122,12 @@ class SharedStringsHelper
} }
$unescapedTextValue = $escaper->unescape($textValue); $unescapedTextValue = $escaper->unescape($textValue);
$this->writeSharedStringToTempFile($unescapedTextValue, $sharedStringIndex);
// The shared string retrieval logic expects each cell data to be on one line only
// Encoding the line feed character allows to preserve this assumption
$lineFeedEncodedTextValue = $this->escapeLineFeed($unescapedTextValue);
$this->writeSharedStringToTempFile($lineFeedEncodedTextValue, $sharedStringIndex);
$sharedStringIndex++; $sharedStringIndex++;
@@ -246,16 +253,39 @@ class SharedStringsHelper
$sharedString = null; $sharedString = null;
if (array_key_exists($indexInFile, $this->inMemoryTempFileContents)) { if (array_key_exists($indexInFile, $this->inMemoryTempFileContents)) {
$sharedString = $this->inMemoryTempFileContents[$indexInFile]; $escapedSharedString = $this->inMemoryTempFileContents[$indexInFile];
$sharedString = $this->unescapeLineFeed($escapedSharedString);
} }
if (!$sharedString) { if ($sharedString === null) {
throw new SharedStringNotFoundException("Shared string not found for index: $sharedStringIndex"); throw new SharedStringNotFoundException("Shared string not found for index: $sharedStringIndex");
} }
return rtrim($sharedString, PHP_EOL); return rtrim($sharedString, PHP_EOL);
} }
/**
* Escapes the line feed character (\n)
*
* @param string $unescapedString
* @return string
*/
private function escapeLineFeed($unescapedString)
{
return str_replace("\n", self::ESCAPED_LINE_FEED_CHARACTER, $unescapedString);
}
/**
* Unescapes the line feed character (\n)
*
* @param string $escapedString
* @return string
*/
private function unescapeLineFeed($escapedString)
{
return str_replace(self::ESCAPED_LINE_FEED_CHARACTER, "\n", $escapedString);
}
/** /**
* Deletes the created temporary folder and all its contents * Deletes the created temporary folder and all its contents
* *
+1 -1
View File
@@ -31,7 +31,7 @@ class Worksheet
*/ */
public function getDataXmlFilePath() public function getDataXmlFilePath()
{ {
return ltrim($this->dataXmlFilePath, DIRECTORY_SEPARATOR); return ltrim($this->dataXmlFilePath, '/');
} }
/** /**
+16 -6
View File
@@ -39,6 +39,7 @@ abstract class AbstractWriter implements WriterInterface
/** /**
* Adds data to the currently openned writer. * Adds data to the currently openned writer.
* The data must be UTF-8 encoded.
* *
* @param array $dataRow Array containing data to be streamed. * @param array $dataRow Array containing data to be streamed.
* Example $dataRow = ['data1', 1234, null, '', 'data5']; * Example $dataRow = ['data1', 1234, null, '', 'data5'];
@@ -88,6 +89,8 @@ abstract class AbstractWriter implements WriterInterface
* Inits the writer and opens it to accept data. * Inits the writer and opens it to accept data.
* By using this method, the data will be outputted directly to the browser. * By using this method, the data will be outputted directly to the browser.
* *
* @codeCoverageIgnore
*
* @param string $outputFileName Name of the output file that will contain the data. If a path is passed in, only the file name will be kept * @param string $outputFileName Name of the output file that will contain the data. If a path is passed in, only the file name will be kept
* @return \Box\Spout\Writer\AbstractWriter * @return \Box\Spout\Writer\AbstractWriter
* @throws \Box\Spout\Common\Exception\IOException If the writer cannot be opened * @throws \Box\Spout\Common\Exception\IOException If the writer cannot be opened
@@ -135,9 +138,11 @@ abstract class AbstractWriter implements WriterInterface
/** /**
* Write given data to the output. New data will be appended to end of stream. * Write given data to the output. New data will be appended to end of stream.
* The data must be UTF-8 encoded.
* *
* @param array $dataRow Array containing data to be streamed. * @param array $dataRow Array containing data to be streamed.
* Example $dataRow = ['data1', 1234, null, '', 'data5']; * If empty, no data is added (i.e. not even as a blank row)
* Example: $dataRow = ['data1', 1234, null, '', 'data5', false];
* *
* @return \Box\Spout\Writer\AbstractWriter * @return \Box\Spout\Writer\AbstractWriter
* @throws \Box\Spout\Writer\Exception\WriterNotOpenedException If this function is called before opening the writer * @throws \Box\Spout\Writer\Exception\WriterNotOpenedException If this function is called before opening the writer
@@ -146,7 +151,10 @@ abstract class AbstractWriter implements WriterInterface
public function addRow(array $dataRow) public function addRow(array $dataRow)
{ {
if ($this->isWriterOpened) { if ($this->isWriterOpened) {
$this->addRowToWriter($dataRow); // empty $dataRow should not add an empty line
if (!empty($dataRow)) {
$this->addRowToWriter($dataRow);
}
} else { } else {
throw new WriterNotOpenedException('The writer needs to be opened before adding row.'); throw new WriterNotOpenedException('The writer needs to be opened before adding row.');
} }
@@ -156,12 +164,14 @@ abstract class AbstractWriter implements WriterInterface
/** /**
* Write given data to the output. New data will be appended to end of stream. * Write given data to the output. New data will be appended to end of stream.
* The data must be UTF-8 encoded.
* *
* @param array $dataRows Array of array containing data to be streamed. * @param array $dataRows Array of array containing data to be streamed.
* Example $dataRow = [ * If a row is empty, it won't be added (i.e. not even as a blank row)
* ['data11', 12, , '', 'data13'], * Example: $dataRows = [
* ['data21', 'data22', null], * ['data11', 12, , '', 'data13'],
* ]; * ['data21', 'data22', null, false],
* ];
* *
* @return \Box\Spout\Writer\AbstractWriter * @return \Box\Spout\Writer\AbstractWriter
* @throws \Box\Spout\Common\Exception\InvalidArgumentException If the input param is not valid * @throws \Box\Spout\Common\Exception\InvalidArgumentException If the input param is not valid
@@ -34,4 +34,38 @@ class CellHelper
return $cellIndex; return $cellIndex;
} }
/**
* @param $value
* @return bool Whether the given value is a non empty string
*/
public static function isNonEmptyString($value)
{
return (gettype($value) === 'string' && $value !== '');
}
/**
* Returns whether the given value is numeric.
* A numeric value is from type "integer" or "double" ("float" is not returned by gettype).
*
* @param $value
* @return bool Whether the given value is numeric
*/
public static function isNumeric($value)
{
$valueType = gettype($value);
return ($valueType === 'integer' || $valueType === 'double');
}
/**
* Returns whether the given value is boolean.
* "true"/"false" and 0/1 are not booleans.
*
* @param $value
* @return bool Whether the given value is boolean
*/
public static function isBoolean($value)
{
return gettype($value) === 'boolean';
}
} }
@@ -5,7 +5,6 @@ namespace Box\Spout\Writer\Internal\XLSX;
use Box\Spout\Writer\Exception\SheetNotFoundException; use Box\Spout\Writer\Exception\SheetNotFoundException;
use Box\Spout\Writer\Helper\XLSX\FileSystemHelper; use Box\Spout\Writer\Helper\XLSX\FileSystemHelper;
use Box\Spout\Writer\Helper\XLSX\SharedStringsHelper; use Box\Spout\Writer\Helper\XLSX\SharedStringsHelper;
use Box\Spout\Writer\Helper\XLSX\ZipHelper;
use Box\Spout\Writer\Sheet; use Box\Spout\Writer\Sheet;
/** /**
+15 -12
View File
@@ -2,6 +2,7 @@
namespace Box\Spout\Writer\Internal\XLSX; namespace Box\Spout\Writer\Internal\XLSX;
use Box\Spout\Common\Exception\InvalidArgumentException;
use Box\Spout\Common\Exception\IOException; use Box\Spout\Common\Exception\IOException;
use Box\Spout\Writer\Helper\XLSX\CellHelper; use Box\Spout\Writer\Helper\XLSX\CellHelper;
@@ -42,7 +43,8 @@ EOD;
/** /**
* @param \Box\Spout\Writer\Sheet $externalSheet The associated "external" sheet * @param \Box\Spout\Writer\Sheet $externalSheet The associated "external" sheet
* @param string $tempFolder Temporary folder where the files to create the XLSX will be stored * @param string $worksheetFilesFolder Temporary folder where the files to create the XLSX will be stored
* @param \Box\Spout\Writer\Helper\XLSX\SharedStringsHelper $sharedStringsHelper Helper for shared strings
* @param bool $shouldUseInlineStrings Whether inline or shared strings should be used * @param bool $shouldUseInlineStrings Whether inline or shared strings should be used
* @throws \Box\Spout\Common\Exception\IOException If the sheet data file cannot be opened for writing * @throws \Box\Spout\Common\Exception\IOException If the sheet data file cannot be opened for writing
*/ */
@@ -118,6 +120,7 @@ EOD;
* Example $dataRow = ['data1', 1234, null, '', 'data5']; * Example $dataRow = ['data1', 1234, null, '', 'data5'];
* @return void * @return void
* @throws \Box\Spout\Common\Exception\IOException If the data cannot be written * @throws \Box\Spout\Common\Exception\IOException If the data cannot be written
* @throws \Box\Spout\Common\Exception\InvalidArgumentException If a cell value's type is not supported
*/ */
public function addRow($dataRow) public function addRow($dataRow)
{ {
@@ -131,19 +134,19 @@ EOD;
$columnIndex = CellHelper::getCellIndexFromColumnIndex($cellNumber); $columnIndex = CellHelper::getCellIndexFromColumnIndex($cellNumber);
$data .= ' <c r="' . $columnIndex . $rowIndex . '"'; $data .= ' <c r="' . $columnIndex . $rowIndex . '"';
if (empty($cellValue)) { if (CellHelper::isNonEmptyString($cellValue)) {
if ($this->shouldUseInlineStrings) {
$data .= ' t="inlineStr"><is><t>' . $this->stringsEscaper->escape($cellValue) . '</t></is></c>' . PHP_EOL;
} else {
$sharedStringId = $this->sharedStringsHelper->writeString($cellValue);
$data .= ' t="s"><v>' . $sharedStringId . '</v></c>' . PHP_EOL;
}
} else if (CellHelper::isNumeric($cellValue) || CellHelper::isBoolean($cellValue)) {
$data .= '><v>' . $cellValue . '</v></c>' . PHP_EOL;
} else if (empty($cellValue)) {
$data .= '/>' . PHP_EOL; $data .= '/>' . PHP_EOL;
} else { } else {
if (is_numeric($cellValue)) { throw new InvalidArgumentException('Trying to add a value with an unsupported type: ' . gettype($cellValue));
$data .= '><v>' . $cellValue . '</v></c>' . PHP_EOL;
} else {
if ($this->shouldUseInlineStrings) {
$data .= ' t="inlineStr"><is><t>' . $this->stringsEscaper->escape($cellValue) . '</t></is></c>' . PHP_EOL;
} else {
$sharedStringId = $this->sharedStringsHelper->writeString($cellValue);
$data .= ' t="s"><v>' . $sharedStringId . '</v></c>' . PHP_EOL;
}
}
} }
$cellNumber++; $cellNumber++;
@@ -0,0 +1,60 @@
<?php
namespace Box\Spout\Reader\Helper\XLSX;
/**
* Class CellHelperTest
*
* @package Box\Spout\Reader\Helper\XLSX
*/
class CellHelperTest extends \PHPUnit_Framework_TestCase
{
/**
* @return void
*/
public function testFillMissingArrayIndexes()
{
$arrayToFill = [1 => 1, 3 => 3];
$filledArray = CellHelper::fillMissingArrayIndexes($arrayToFill, 'FILL');
$expectedFilledArray = ['FILL', 1, 'FILL', 3];
$this->assertEquals($expectedFilledArray, $filledArray);
}
/**
* @return array
*/
public function dataProviderForTestGetColumnIndexFromCellIndex()
{
return [
['A1', 0],
['Z3', 25],
['AA5', 26],
['AB24', 27],
['BC5', 54],
['BCZ99', 1455],
];
}
/**
* @dataProvider dataProviderForTestGetColumnIndexFromCellIndex
*
* @param string $cellIndex
* @param int $expectedColumnIndex
* @return void
*/
public function testGetColumnIndexFromCellIndex($cellIndex, $expectedColumnIndex)
{
$this->assertEquals($expectedColumnIndex, CellHelper::getColumnIndexFromCellIndex($cellIndex));
}
/**
* @expectedException \Box\Spout\Common\Exception\InvalidArgumentException
*
* @return void
*/
public function testGetColumnIndexFromCellIndexShouldThrowIfInvalidCellIndex()
{
CellHelper::getColumnIndexFromCellIndex('InvalidCellIndex');
}
}
@@ -46,7 +46,7 @@ class SharedStringsHelperTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(1, count($filesInTempFolder), 'One temp file should have been created in the temp folder.'); $this->assertEquals(1, count($filesInTempFolder), 'One temp file should have been created in the temp folder.');
$tempFileContents = file_get_contents($filesInTempFolder[0]); $tempFileContents = file_get_contents($filesInTempFolder[0]);
$tempFileContentsPerLine = explode("\n", $tempFileContents); $tempFileContentsPerLine = explode(PHP_EOL, $tempFileContents);
$this->assertEquals('s1--A1', $tempFileContentsPerLine[0]); $this->assertEquals('s1--A1', $tempFileContentsPerLine[0]);
$this->assertEquals('s1--E5', $tempFileContentsPerLine[24]); $this->assertEquals('s1--E5', $tempFileContentsPerLine[24]);
@@ -96,4 +96,23 @@ class SharedStringsHelperTest extends \PHPUnit_Framework_TestCase
$sharedString = $this->sharedStringsHelper->getStringAtIndex(24); $sharedString = $this->sharedStringsHelper->getStringAtIndex(24);
$this->assertEquals('s1--E5', $sharedString); $this->assertEquals('s1--E5', $sharedString);
} }
/**
* @return void
*/
public function testGetStringAtIndexShouldWorkWithMultilineStrings()
{
$resourcePath = $this->getResourcePath('one_sheet_with_shared_multiline_strings.xlsx');
$sharedStringsHelper = new SharedStringsHelper($resourcePath);
$sharedStringsHelper->extractSharedStrings();
$sharedString = $sharedStringsHelper->getStringAtIndex(0);
$this->assertEquals("s1\nA1", $sharedString);
$sharedString = $sharedStringsHelper->getStringAtIndex(24);
$this->assertEquals("s1\nE5", $sharedString);
$sharedStringsHelper->cleanup();
}
} }
+21
View File
@@ -0,0 +1,21 @@
<?php
namespace Box\Spout\Reader;
/**
* Class ReaderFactoryTest
*
* @package Box\Spout\Writer
*/
class ReaderFactoryTest extends \PHPUnit_Framework_TestCase
{
/**
* @expectedException \Box\Spout\Common\Exception\UnsupportedTypeException
*
* @return void
*/
public function testCreateReaderShouldThrowWithUnsupportedType()
{
ReaderFactory::create('unsupportedType');
}
}
+27 -1
View File
@@ -140,6 +140,32 @@ class XLSXTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($expectedRows, $allRows); $this->assertEquals($expectedRows, $allRows);
} }
/**
* @return void
*/
public function testReadShouldSupportEmptySharedString()
{
$allRows = $this->getAllRowsForFile('sheet_with_empty_shared_string.xlsx');
$expectedRows = [
['s1--A1', '', 's1--C1'],
];
$this->assertEquals($expectedRows, $allRows);
}
/**
* @return void
*/
public function testReadShouldPreserveSpaceIfSpecified()
{
$allRows = $this->getAllRowsForFile('sheet_with_preserve_space_shared_strings.xlsx');
$expectedRows = [
[' s1--A1', 's1--B1 ', ' s1--C1 '],
];
$this->assertEquals($expectedRows, $allRows);
}
/** /**
* @return void * @return void
*/ */
@@ -158,7 +184,7 @@ class XLSXTest extends \PHPUnit_Framework_TestCase
{ {
$allRows = $this->getAllRowsForFile('billion_laughs_test_file.xlsx'); $allRows = $this->getAllRowsForFile('billion_laughs_test_file.xlsx');
$expectedMaxMemoryUsage = 10 * 1024 * 1024; // 10MB $expectedMaxMemoryUsage = 20 * 1024 * 1024; // 20MB
$this->assertLessThan($expectedMaxMemoryUsage, memory_get_peak_usage(true), 'Entities should not be expanded and therefore consume all the memory.'); $this->assertLessThan($expectedMaxMemoryUsage, memory_get_peak_usage(true), 'Entities should not be expanded and therefore consume all the memory.');
$expectedFirstRow = ['s1--A1', 's1--B1', 's1--C1', 's1--D1', 's1--E1']; $expectedFirstRow = ['s1--A1', 's1--B1', 's1--C1', 's1--D1', 's1--E1'];
+27 -1
View File
@@ -49,6 +49,16 @@ class CSVTest extends \PHPUnit_Framework_TestCase
$writer->close(); $writer->close();
} }
/**
* @expectedException \Box\Spout\Common\Exception\InvalidArgumentException
*/
public function testAddRowsShouldThrowExceptionIfRowsAreNotArrayOfArrays()
{
$writer = WriterFactory::create(Type::CSV);
$writer->addRows(['csv--11', 'csv--12']);
$writer->close();
}
/** /**
* @return void * @return void
*/ */
@@ -76,6 +86,22 @@ class CSVTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('csv--11,,csv--13', $writtenContent, 'The null values should be replaced by empty values'); $this->assertEquals('csv--11,,csv--13', $writtenContent, 'The null values should be replaced by empty values');
} }
/**
* @return void
*/
public function testWriteShouldSkipEmptyRows()
{
$allRows = [
['csv--11', 'csv--12'],
[],
['csv--31', 'csv--32'],
];
$writtenContent = $this->writeToCsvFileAndReturnWrittenContent($allRows, 'csv_with_empty_rows.csv');
$writtenContent = $this->trimWrittenContent($writtenContent);
$this->assertEquals("csv--11,csv--12\ncsv--31,csv--32", $writtenContent, 'Empty rows should be skipped');
}
/** /**
* @return void * @return void
*/ */
@@ -135,6 +161,6 @@ class CSVTest extends \PHPUnit_Framework_TestCase
private function trimWrittenContent($writtenContent) private function trimWrittenContent($writtenContent)
{ {
// remove line feeds and UTF-8 BOM // remove line feeds and UTF-8 BOM
return trim($writtenContent, "\n" . CSV::UTF8_BOM); return trim($writtenContent, PHP_EOL . CSV::UTF8_BOM);
} }
} }
@@ -34,4 +34,62 @@ class CellHelperTest extends \PHPUnit_Framework_TestCase
{ {
$this->assertEquals($expectedCellIndex, CellHelper::getCellIndexFromColumnIndex($columnIndex)); $this->assertEquals($expectedCellIndex, CellHelper::getCellIndexFromColumnIndex($columnIndex));
} }
/**
* @return array
*/
public function testIsNonEmptyString()
{
$this->assertTrue(CellHelper::isNonEmptyString("string"));
$this->assertFalse(CellHelper::isNonEmptyString(""));
$this->assertFalse(CellHelper::isNonEmptyString(0));
$this->assertFalse(CellHelper::isNonEmptyString(1));
$this->assertFalse(CellHelper::isNonEmptyString(true));
$this->assertFalse(CellHelper::isNonEmptyString(false));
$this->assertFalse(CellHelper::isNonEmptyString(["string"]));
$this->assertFalse(CellHelper::isNonEmptyString(new \stdClass()));
$this->assertFalse(CellHelper::isNonEmptyString(null));
}
/**
* @return array
*/
public function testIsNumeric()
{
$this->assertTrue(CellHelper::isNumeric(0));
$this->assertTrue(CellHelper::isNumeric(10));
$this->assertTrue(CellHelper::isNumeric(10.1));
$this->assertTrue(CellHelper::isNumeric(10.10000000000000000000001));
$this->assertTrue(CellHelper::isNumeric(0x539));
$this->assertTrue(CellHelper::isNumeric(02471));
$this->assertTrue(CellHelper::isNumeric(0b10100111001));
$this->assertTrue(CellHelper::isNumeric(1337e0));
$this->assertFalse(CellHelper::isNumeric("0"));
$this->assertFalse(CellHelper::isNumeric("42"));
$this->assertFalse(CellHelper::isNumeric(true));
$this->assertFalse(CellHelper::isNumeric([2]));
$this->assertFalse(CellHelper::isNumeric(new \stdClass()));
$this->assertFalse(CellHelper::isNumeric(null));
}
/**
* @return array
*/
public function testIsBoolean()
{
$this->assertTrue(CellHelper::isBoolean(true));
$this->assertTrue(CellHelper::isBoolean(false));
$this->assertFalse(CellHelper::isBoolean(0));
$this->assertFalse(CellHelper::isBoolean(1));
$this->assertFalse(CellHelper::isBoolean("0"));
$this->assertFalse(CellHelper::isBoolean("1"));
$this->assertFalse(CellHelper::isBoolean("true"));
$this->assertFalse(CellHelper::isBoolean("false"));
$this->assertFalse(CellHelper::isBoolean([true]));
$this->assertFalse(CellHelper::isBoolean(new \stdClass()));
$this->assertFalse(CellHelper::isBoolean(null));
}
} }
+21
View File
@@ -0,0 +1,21 @@
<?php
namespace Box\Spout\Writer;
/**
* Class WriterFactoryTest
*
* @package Box\Spout\Writer
*/
class WriterFactoryTest extends \PHPUnit_Framework_TestCase
{
/**
* @expectedException \Box\Spout\Common\Exception\UnsupportedTypeException
*
* @return void
*/
public function testCreateWriterShouldThrowWithUnsupportedType()
{
WriterFactory::create('unsupportedType');
}
}
+52 -21
View File
@@ -49,6 +49,19 @@ class XLSXTest extends \PHPUnit_Framework_TestCase
$writer->close(); $writer->close();
} }
/**
* @expectedException \Box\Spout\Common\Exception\InvalidArgumentException
*/
public function testAddRowShouldThrowExceptionIfUnsupportedDataTypePassedIn()
{
$fileName = 'test_add_row_should_throw_exception_if_unsupported_data_type_passed_in.xlsx';
$dataRows = [
[new \stdClass()],
];
$this->writeToXLSXFile($dataRows, $fileName);
}
/** /**
* @return void * @return void
*/ */
@@ -96,7 +109,7 @@ class XLSXTest extends \PHPUnit_Framework_TestCase
*/ */
public function testAddRowShouldWriteGivenDataToSheetUsingInlineStrings() public function testAddRowShouldWriteGivenDataToSheetUsingInlineStrings()
{ {
$fileName = 'test_add_row_should_write_given_data_to_sheet.xlsx'; $fileName = 'test_add_row_should_write_given_data_to_sheet_using_inline_strings.xlsx';
$dataRows = [ $dataRows = [
['xlsx--11', 'xlsx--12'], ['xlsx--11', 'xlsx--12'],
['xlsx--21', 'xlsx--22', 'xlsx--23'], ['xlsx--21', 'xlsx--22', 'xlsx--23'],
@@ -106,7 +119,7 @@ class XLSXTest extends \PHPUnit_Framework_TestCase
foreach ($dataRows as $dataRow) { foreach ($dataRows as $dataRow) {
foreach ($dataRow as $cellValue) { foreach ($dataRow as $cellValue) {
$this->assertInlineStringWasWrittenToSheet($fileName, 1, $cellValue); $this->assertInlineDataWasWrittenToSheet($fileName, 1, $cellValue);
} }
} }
} }
@@ -116,7 +129,7 @@ class XLSXTest extends \PHPUnit_Framework_TestCase
*/ */
public function testAddRowShouldWriteGivenDataToTwoSheetUsingInlineStrings() public function testAddRowShouldWriteGivenDataToTwoSheetUsingInlineStrings()
{ {
$fileName = 'test_add_row_should_write_given_data_to_sheet.xlsx'; $fileName = 'test_add_row_should_write_given_data_to_two_sheets_using_inline_strings.xlsx';
$dataRows = [ $dataRows = [
['xlsx--11', 'xlsx--12'], ['xlsx--11', 'xlsx--12'],
['xlsx--21', 'xlsx--22', 'xlsx--23'], ['xlsx--21', 'xlsx--22', 'xlsx--23'],
@@ -128,7 +141,7 @@ class XLSXTest extends \PHPUnit_Framework_TestCase
for ($i = 1; $i <= $numSheets; $i++) { for ($i = 1; $i <= $numSheets; $i++) {
foreach ($dataRows as $dataRow) { foreach ($dataRows as $dataRow) {
foreach ($dataRow as $cellValue) { foreach ($dataRow as $cellValue) {
$this->assertInlineStringWasWrittenToSheet($fileName, $numSheets, $cellValue); $this->assertInlineDataWasWrittenToSheet($fileName, $numSheets, $cellValue);
} }
} }
} }
@@ -139,7 +152,7 @@ class XLSXTest extends \PHPUnit_Framework_TestCase
*/ */
public function testAddRowShouldWriteGivenDataToSheetUsingSharedStrings() public function testAddRowShouldWriteGivenDataToSheetUsingSharedStrings()
{ {
$fileName = 'test_add_row_should_write_given_data_to_sheet.xlsx'; $fileName = 'test_add_row_should_write_given_data_to_sheet_using_shared_strings.xlsx';
$dataRows = [ $dataRows = [
['xlsx--11', 'xlsx--12'], ['xlsx--11', 'xlsx--12'],
['xlsx--21', 'xlsx--22', 'xlsx--23'], ['xlsx--21', 'xlsx--22', 'xlsx--23'],
@@ -159,7 +172,7 @@ class XLSXTest extends \PHPUnit_Framework_TestCase
*/ */
public function testAddRowShouldWriteGivenDataToTwoSheetUsingSharedStrings() public function testAddRowShouldWriteGivenDataToTwoSheetUsingSharedStrings()
{ {
$fileName = 'test_add_row_should_write_given_data_to_two_sheet_using_shared_strings.xlsx'; $fileName = 'test_add_row_should_write_given_data_to_two_sheets_using_shared_strings.xlsx';
$dataRows = [ $dataRows = [
['xlsx--11', 'xlsx--12'], ['xlsx--11', 'xlsx--12'],
['xlsx--21', 'xlsx--22', 'xlsx--23'], ['xlsx--21', 'xlsx--22', 'xlsx--23'],
@@ -177,6 +190,24 @@ class XLSXTest extends \PHPUnit_Framework_TestCase
} }
} }
/**
* @return void
*/
public function testAddRowShouldSupportMultipleTypesOfData()
{
$fileName = 'test_add_row_should_support_multiple_types_of_data.xlsx';
$dataRows = [
['xlsx--11', true, '', 0, 10.2, null],
];
$this->writeToXLSXFile($dataRows, $fileName, $shouldUseInlineStrings = false);
$this->assertSharedStringWasWritten($fileName, 'xlsx--11');
$this->assertInlineDataWasWrittenToSheet($fileName, 1, 1); // true is converted to 1
$this->assertInlineDataWasWrittenToSheet($fileName, 1, 0);
$this->assertInlineDataWasWrittenToSheet($fileName, 1, 10.2);
}
/** /**
* @return void * @return void
*/ */
@@ -219,17 +250,17 @@ class XLSXTest extends \PHPUnit_Framework_TestCase
foreach ($dataRowsSheet1 as $dataRow) { foreach ($dataRowsSheet1 as $dataRow) {
foreach ($dataRow as $cellValue) { foreach ($dataRow as $cellValue) {
$this->assertInlineStringWasWrittenToSheet($fileName, 1, $cellValue, 'Data should have been written in Sheet 1'); $this->assertInlineDataWasWrittenToSheet($fileName, 1, $cellValue, 'Data should have been written in Sheet 1');
} }
} }
foreach ($dataRowsSheet2 as $dataRow) { foreach ($dataRowsSheet2 as $dataRow) {
foreach ($dataRow as $cellValue) { foreach ($dataRow as $cellValue) {
$this->assertInlineStringWasWrittenToSheet($fileName, 2, $cellValue, 'Data should have been written in Sheet 2'); $this->assertInlineDataWasWrittenToSheet($fileName, 2, $cellValue, 'Data should have been written in Sheet 2');
} }
} }
foreach ($dataRowsSheet1Again as $dataRow) { foreach ($dataRowsSheet1Again as $dataRow) {
foreach ($dataRow as $cellValue) { foreach ($dataRow as $cellValue) {
$this->assertInlineStringWasWrittenToSheet($fileName, 1, $cellValue, 'Data should have been written in Sheet 1'); $this->assertInlineDataWasWrittenToSheet($fileName, 1, $cellValue, 'Data should have been written in Sheet 1');
} }
} }
} }
@@ -252,8 +283,8 @@ class XLSXTest extends \PHPUnit_Framework_TestCase
$writer = $this->writeToXLSXFile($dataRows, $fileName, true, $shouldCreateSheetsAutomatically = true); $writer = $this->writeToXLSXFile($dataRows, $fileName, true, $shouldCreateSheetsAutomatically = true);
$this->assertEquals(2, count($writer->getSheets()), '2 sheets should have been created.'); $this->assertEquals(2, count($writer->getSheets()), '2 sheets should have been created.');
$this->assertInlineStringWasNotWrittenToSheet($fileName, 1, 'xlsx--sheet2--11'); $this->assertInlineDataWasNotWrittenToSheet($fileName, 1, 'xlsx--sheet2--11');
$this->assertInlineStringWasWrittenToSheet($fileName, 2, 'xlsx--sheet2--11'); $this->assertInlineDataWasWrittenToSheet($fileName, 2, 'xlsx--sheet2--11');
\ReflectionHelper::reset(); \ReflectionHelper::reset();
} }
@@ -276,7 +307,7 @@ class XLSXTest extends \PHPUnit_Framework_TestCase
$writer = $this->writeToXLSXFile($dataRows, $fileName, true, $shouldCreateSheetsAutomatically = false); $writer = $this->writeToXLSXFile($dataRows, $fileName, true, $shouldCreateSheetsAutomatically = false);
$this->assertEquals(1, count($writer->getSheets()), 'Only 1 sheet should have been created.'); $this->assertEquals(1, count($writer->getSheets()), 'Only 1 sheet should have been created.');
$this->assertInlineStringWasNotWrittenToSheet($fileName, 1, 'xlsx--sheet1--31'); $this->assertInlineDataWasNotWrittenToSheet($fileName, 1, 'xlsx--sheet1--31');
\ReflectionHelper::reset(); \ReflectionHelper::reset();
} }
@@ -293,8 +324,8 @@ class XLSXTest extends \PHPUnit_Framework_TestCase
$this->writeToXLSXFile($dataRows, $fileName); $this->writeToXLSXFile($dataRows, $fileName);
$this->assertInlineStringWasWrittenToSheet($fileName, 1, 'I&#039;m in &quot;great&quot; mood', 'Quotes should be escaped'); $this->assertInlineDataWasWrittenToSheet($fileName, 1, 'I&#039;m in &quot;great&quot; mood', 'Quotes should be escaped');
$this->assertInlineStringWasWrittenToSheet($fileName, 1, 'This &lt;must&gt; be escaped &amp; tested', '<, > and & should be escaped'); $this->assertInlineDataWasWrittenToSheet($fileName, 1, 'This &lt;must&gt; be escaped &amp; tested', '<, > and & should be escaped');
} }
/** /**
@@ -309,7 +340,7 @@ class XLSXTest extends \PHPUnit_Framework_TestCase
$this->writeToXLSXFile($dataRows, $fileName); $this->writeToXLSXFile($dataRows, $fileName);
$this->assertInlineStringWasWrittenToSheet($fileName, 1, 'control&#039;s _x0015_ &quot;character&quot;'); $this->assertInlineDataWasWrittenToSheet($fileName, 1, 'control&#039;s _x0015_ &quot;character&quot;');
} }
@@ -371,33 +402,33 @@ class XLSXTest extends \PHPUnit_Framework_TestCase
/** /**
* @param string $fileName * @param string $fileName
* @param int $sheetNumber * @param int $sheetNumber
* @param string $inlineString * @param mixed $inlineData
* @param string $message * @param string $message
* @return void * @return void
*/ */
private function assertInlineStringWasWrittenToSheet($fileName, $sheetNumber, $inlineString, $message = '') private function assertInlineDataWasWrittenToSheet($fileName, $sheetNumber, $inlineData, $message = '')
{ {
$resourcePath = $this->getGeneratedResourcePath($fileName); $resourcePath = $this->getGeneratedResourcePath($fileName);
$pathToSheetFile = $resourcePath . '#xl/worksheets/sheet' . $sheetNumber . '.xml'; $pathToSheetFile = $resourcePath . '#xl/worksheets/sheet' . $sheetNumber . '.xml';
$xmlContents = file_get_contents('zip://' . $pathToSheetFile); $xmlContents = file_get_contents('zip://' . $pathToSheetFile);
$this->assertContains($inlineString, $xmlContents, $message); $this->assertContains((string)$inlineData, $xmlContents, $message);
} }
/** /**
* @param string $fileName * @param string $fileName
* @param int $sheetNumber * @param int $sheetNumber
* @param string $inlineString * @param mixed $inlineData
* @param string $message * @param string $message
* @return void * @return void
*/ */
private function assertInlineStringWasNotWrittenToSheet($fileName, $sheetNumber, $inlineString, $message = '') private function assertInlineDataWasNotWrittenToSheet($fileName, $sheetNumber, $inlineData, $message = '')
{ {
$resourcePath = $this->getGeneratedResourcePath($fileName); $resourcePath = $this->getGeneratedResourcePath($fileName);
$pathToSheetFile = $resourcePath . '#xl/worksheets/sheet' . $sheetNumber . '.xml'; $pathToSheetFile = $resourcePath . '#xl/worksheets/sheet' . $sheetNumber . '.xml';
$xmlContents = file_get_contents('zip://' . $pathToSheetFile); $xmlContents = file_get_contents('zip://' . $pathToSheetFile);
$this->assertNotContains($inlineString, $xmlContents, $message); $this->assertNotContains((string)$inlineData, $xmlContents, $message);
} }
/** /**