First external release
This commit is contained in:
commit
5e199009e6
81 files changed
+5447
No files matched your search
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace Box\Spout\Common\Escaper;
|
||||
|
||||
/**
|
||||
* Class XLSXTest
|
||||
*
|
||||
* @package Box\Spout\Common\Escaper
|
||||
*/
|
||||
class XLSXTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function dataProviderForTestEscape()
|
||||
{
|
||||
return [
|
||||
['test', 'test'],
|
||||
['adam\'s "car"', 'adam's "car"'],
|
||||
[chr(0), '_x0000_'],
|
||||
['_x0000_', '_x005F_x0000_'],
|
||||
[chr(21), '_x0015_'],
|
||||
['control '.chr(21).' character', 'control _x0015_ character'],
|
||||
['control\'s '.chr(21).' "character"', 'control's _x0015_ "character"'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataProviderForTestEscape
|
||||
*
|
||||
* @param string $stringToEscape
|
||||
* @param string $expectedEscapedString
|
||||
* @return void
|
||||
*/
|
||||
public function testEscape($stringToEscape, $expectedEscapedString)
|
||||
{
|
||||
$escaper = new \Box\Spout\Common\Escaper\XLSX();
|
||||
$escapedString = $escaper->escape($stringToEscape);
|
||||
|
||||
$this->assertEquals($expectedEscapedString, $escapedString, 'Incorrect escaped string');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function dataProviderForTestUnescape()
|
||||
{
|
||||
return [
|
||||
['test', 'test'],
|
||||
['adam's "car"', 'adam\'s "car"'],
|
||||
['_x0000_', chr(0)],
|
||||
['_x005F_x0000_', '_x0000_'],
|
||||
['_x0015_', chr(21)],
|
||||
['control _x0015_ character', 'control '.chr(21).' character'],
|
||||
['control's _x0015_ "character"', 'control\'s '.chr(21).' "character"'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataProviderForTestUnescape
|
||||
*
|
||||
* @param string $stringToUnescape
|
||||
* @param string $expectedUnescapedString
|
||||
* @return void
|
||||
*/
|
||||
public function testUnescape($stringToUnescape, $expectedUnescapedString)
|
||||
{
|
||||
$escaper = new \Box\Spout\Common\Escaper\XLSX();
|
||||
$unescapedString = $escaper->unescape($stringToUnescape);
|
||||
|
||||
$this->assertEquals($expectedUnescapedString, $unescapedString, 'Incorrect escaped string');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace Box\Spout\Common\Helper;
|
||||
|
||||
/**
|
||||
* Class FileSystemHelperTest
|
||||
*
|
||||
* @package Box\Spout\Common\Helper
|
||||
*/
|
||||
class FileSystemHelperTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/** @var \Box\Spout\Writer\Helper\XLSX\FileSystemHelper */
|
||||
protected $fileSystemHelper;
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function setUp()
|
||||
{
|
||||
$baseFolder = '/tmp/base_folder';
|
||||
$this->fileSystemHelper = new FileSystemHelper($baseFolder);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Box\Spout\Common\Exception\IOException
|
||||
* @return void
|
||||
*/
|
||||
public function testCreateFolderShouldThrowExceptionIfOutsideOfBaseFolder()
|
||||
{
|
||||
$this->fileSystemHelper->createFolder('/tmp/folder_outside_base_folder', 'folder_name');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Box\Spout\Common\Exception\IOException
|
||||
* @return void
|
||||
*/
|
||||
public function testCreateFileWithContentsShouldThrowExceptionIfOutsideOfBaseFolder()
|
||||
{
|
||||
$this->fileSystemHelper->createFileWithContents('/tmp/folder_outside_base_folder', 'file_name', 'contents');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Box\Spout\Common\Exception\IOException
|
||||
* @return void
|
||||
*/
|
||||
public function testDeleteFileShouldThrowExceptionIfOutsideOfBaseFolder()
|
||||
{
|
||||
$this->fileSystemHelper->deleteFile('/tmp/folder_outside_base_folder/file_name');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Box\Spout\Common\Exception\IOException
|
||||
* @return void
|
||||
*/
|
||||
public function testDeleteFolderRecursivelyShouldThrowExceptionIfOutsideOfBaseFolder()
|
||||
{
|
||||
$this->fileSystemHelper->deleteFolderRecursively('/tmp/folder_outside_base_folder');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
|
||||
namespace Box\Spout\Reader;
|
||||
|
||||
use Box\Spout\Common\Type;
|
||||
use Box\Spout\TestUsingResource;
|
||||
|
||||
/**
|
||||
* Class CSVTest
|
||||
*
|
||||
* @package Box\Spout\Reader
|
||||
*/
|
||||
class CSVTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
use TestUsingResource;
|
||||
|
||||
/**
|
||||
* @expectedException \Box\Spout\Common\Exception\IOException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testReadShouldThrowExceptionIfFileDoesNotExist()
|
||||
{
|
||||
$this->getAllRowsForFile('/path/to/fake/file.csv');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testReadStandardCSV()
|
||||
{
|
||||
$allRows = $this->getAllRowsForFile('csv_standard.csv');
|
||||
|
||||
$expectedRows = [
|
||||
['csv--11', 'csv--12', 'csv--13'],
|
||||
['csv--21', 'csv--22', 'csv--23'],
|
||||
['csv--31', 'csv--32', 'csv--33'],
|
||||
];
|
||||
$this->assertEquals($expectedRows, $allRows);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testReadShouldNotStopAtCommaIfEnclosed()
|
||||
{
|
||||
$allRows = $this->getAllRowsForFile('csv_with_comma_enclosed.csv');
|
||||
$this->assertEquals('This is, a comma', $allRows[0][0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testReadShouldKeepEmptyCells()
|
||||
{
|
||||
$allRows = $this->getAllRowsForFile('csv_with_empty_cells.csv');
|
||||
|
||||
$expectedRows = [
|
||||
['csv--11', 'csv--12', 'csv--13'],
|
||||
['csv--21', '', 'csv--23'],
|
||||
['csv--31', 'csv--32', ''],
|
||||
];
|
||||
$this->assertEquals($expectedRows, $allRows);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testReadShouldSkipEmptyLines()
|
||||
{
|
||||
$allRows = $this->getAllRowsForFile('csv_with_empty_line.csv');
|
||||
|
||||
$expectedRows = [
|
||||
['csv--11', 'csv--12', 'csv--13'],
|
||||
['csv--31', 'csv--32', 'csv--33'],
|
||||
];
|
||||
$this->assertEquals($expectedRows, $allRows);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testReadShouldHaveTheRightNumberOfCells()
|
||||
{
|
||||
$allRows = $this->getAllRowsForFile('csv_with_different_cells_number.csv');
|
||||
|
||||
$expectedRows = [
|
||||
['csv--11', 'csv--12', 'csv--13'],
|
||||
['csv--21', 'csv--22'],
|
||||
['csv--31'],
|
||||
];
|
||||
$this->assertEquals($expectedRows, $allRows);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testReadShouldSupportCustomFieldDelimiter()
|
||||
{
|
||||
$allRows = $this->getAllRowsForFile('csv_delimited_with_pipes.csv', '|');
|
||||
|
||||
$expectedRows = [
|
||||
['csv--11', 'csv--12', 'csv--13'],
|
||||
['csv--21', 'csv--22', 'csv--23'],
|
||||
['csv--31', 'csv--32', 'csv--33'],
|
||||
];
|
||||
$this->assertEquals($expectedRows, $allRows);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testReadShouldSupportCustomFieldEnclosure()
|
||||
{
|
||||
$allRows = $this->getAllRowsForFile('csv_text_enclosed_with_pound.csv', ',', '#');
|
||||
$this->assertEquals('This is, a comma', $allRows[0][0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testReadShouldSkipUtf8Bom()
|
||||
{
|
||||
$allRows = $this->getAllRowsForFile('csv_with_utf8_bom.csv');
|
||||
|
||||
$expectedRows = [
|
||||
['csv--11', 'csv--12', 'csv--13'],
|
||||
['csv--21', 'csv--22', 'csv--23'],
|
||||
];
|
||||
$this->assertEquals($expectedRows, $allRows);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fileName
|
||||
* @param string|void $fieldDelimiter
|
||||
* @param string|void $fieldEnclosure
|
||||
* @return array All the read rows the given file
|
||||
*/
|
||||
private function getAllRowsForFile($fileName, $fieldDelimiter = ",", $fieldEnclosure = '"')
|
||||
{
|
||||
$allRows = [];
|
||||
$resourcePath = $this->getResourcePath($fileName);
|
||||
|
||||
$reader = ReaderFactory::create(Type::CSV);
|
||||
$reader->setFieldDelimiter($fieldDelimiter);
|
||||
$reader->setFieldEnclosure($fieldEnclosure);
|
||||
|
||||
$reader->open($resourcePath);
|
||||
|
||||
while ($reader->hasNextRow()) {
|
||||
$allRows[] = $reader->nextRow();
|
||||
}
|
||||
|
||||
$reader->close();
|
||||
|
||||
return $allRows;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
namespace Box\Spout\Reader\Helper\XLSX;
|
||||
|
||||
use Box\Spout\TestUsingResource;
|
||||
|
||||
/**
|
||||
* Class SharedStringsHelperTest
|
||||
*
|
||||
* @package Box\Spout\Reader\Helper\XLSX
|
||||
*/
|
||||
class SharedStringsHelperTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
use TestUsingResource;
|
||||
|
||||
/** @var SharedStringsHelper */
|
||||
private $sharedStringsHelper;
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function setUp()
|
||||
{
|
||||
$resourcePath = $this->getResourcePath('one_sheet_with_shared_strings.xlsx');
|
||||
$this->sharedStringsHelper = new SharedStringsHelper($resourcePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function tearDown()
|
||||
{
|
||||
$this->sharedStringsHelper->cleanup();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testExtractSharedStringsShouldCreateTempFileWithSharedStrings()
|
||||
{
|
||||
$this->sharedStringsHelper->extractSharedStrings();
|
||||
|
||||
$tempFolder = \ReflectionHelper::getValueOnObject($this->sharedStringsHelper, 'tempFolder');
|
||||
|
||||
$filesInTempFolder = $this->getFilesInFolder($tempFolder);
|
||||
$this->assertEquals(1, count($filesInTempFolder), 'One temp file should have been created in the temp folder.');
|
||||
|
||||
$tempFileContents = file_get_contents($filesInTempFolder[0]);
|
||||
$tempFileContentsPerLine = explode("\n", $tempFileContents);
|
||||
|
||||
$this->assertEquals('s1--A1', $tempFileContentsPerLine[0]);
|
||||
$this->assertEquals('s1--E5', $tempFileContentsPerLine[24]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all files that are in the given folder.
|
||||
* It does not include "." and ".." and is not recursive.
|
||||
*
|
||||
* @param string $folderPath
|
||||
* @return array
|
||||
*/
|
||||
private function getFilesInFolder($folderPath)
|
||||
{
|
||||
$files = [];
|
||||
$directoryIterator = new \DirectoryIterator($folderPath);
|
||||
|
||||
foreach ($directoryIterator as $fileInfo) {
|
||||
if ($fileInfo->isFile()) {
|
||||
$files[] = $fileInfo->getPathname();
|
||||
}
|
||||
}
|
||||
|
||||
return $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Box\Spout\Reader\Exception\SharedStringNotFoundException
|
||||
* @return void
|
||||
*/
|
||||
public function testGetStringAtIndexShouldThrowExceptionIfStringNotFound()
|
||||
{
|
||||
$this->sharedStringsHelper->extractSharedStrings();
|
||||
$this->sharedStringsHelper->getStringAtIndex(PHP_INT_MAX);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testGetStringAtIndexShouldReturnTheCorrectStringIfFound()
|
||||
{
|
||||
$this->sharedStringsHelper->extractSharedStrings();
|
||||
|
||||
$sharedString = $this->sharedStringsHelper->getStringAtIndex(0);
|
||||
$this->assertEquals('s1--A1', $sharedString);
|
||||
|
||||
$sharedString = $this->sharedStringsHelper->getStringAtIndex(24);
|
||||
$this->assertEquals('s1--E5', $sharedString);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
<?php
|
||||
|
||||
namespace Box\Spout\Reader;
|
||||
|
||||
use Box\Spout\Common\Type;
|
||||
use Box\Spout\TestUsingResource;
|
||||
|
||||
/**
|
||||
* Class XLSXTest
|
||||
*
|
||||
* @package Box\Spout\Reader
|
||||
*/
|
||||
class XLSXTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
use TestUsingResource;
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function dataProviderForTestReadShouldThrowException()
|
||||
{
|
||||
return [
|
||||
['/path/to/fake/file.xlsx'],
|
||||
['file_with_no_sheets_in_content_types.xlsx'],
|
||||
['file_corrupted.xlsx'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataProviderForTestReadShouldThrowException
|
||||
* @expectedException \Box\Spout\Common\Exception\IOException
|
||||
*
|
||||
* @param string $filePath
|
||||
* @return void
|
||||
*/
|
||||
public function testReadShouldThrowException($filePath)
|
||||
{
|
||||
$this->getAllRowsForFile($filePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function dataProviderForTestReadForAllWorksheets()
|
||||
{
|
||||
return [
|
||||
['one_sheet_with_shared_strings.xlsx', 5, 5],
|
||||
['one_sheet_with_inline_strings.xlsx', 5, 5],
|
||||
['two_sheets_with_shared_strings.xlsx', 10, 5],
|
||||
['two_sheets_with_inline_strings.xlsx', 10, 5]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataProviderForTestReadForAllWorksheets
|
||||
*
|
||||
* @param string $resourceName
|
||||
* @param int $expectedNumOfRows
|
||||
* @param int $expectedNumOfCellsPerRow
|
||||
* @return void
|
||||
*/
|
||||
public function testReadForAllWorksheets($resourceName, $expectedNumOfRows, $expectedNumOfCellsPerRow)
|
||||
{
|
||||
$allRows = $this->getAllRowsForFile($resourceName);
|
||||
|
||||
$this->assertEquals($expectedNumOfRows, count($allRows), "There should be $expectedNumOfRows rows");
|
||||
foreach ($allRows as $row) {
|
||||
$this->assertEquals($expectedNumOfCellsPerRow, count($row), "There should be $expectedNumOfCellsPerRow cells for every row");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testReadShouldKeepEmptyCellsAtTheEndIfDimensionsSpecified()
|
||||
{
|
||||
$allRows = $this->getAllRowsForFile('sheet_without_dimensions_but_spans_and_empty_cells.xlsx');
|
||||
|
||||
$this->assertEquals(2, count($allRows), 'There should be 2 rows');
|
||||
foreach ($allRows as $row) {
|
||||
$this->assertEquals(5, count($row), 'There should be 5 cells for every row, because empty rows should be preserved');
|
||||
}
|
||||
|
||||
$expectedRows = [
|
||||
['s1--A1', 's1--B1', 's1--C1', 's1--D1', 's1--E1'],
|
||||
['s1--A2', 's1--B2', 's1--C2', '', ''],
|
||||
];
|
||||
$this->assertEquals($expectedRows, $allRows);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testReadShouldKeepEmptyCellsAtTheEndIfNoDimensionsButSpansSpecified()
|
||||
{
|
||||
$allRows = $this->getAllRowsForFile('sheet_without_dimensions_and_empty_cells.xlsx');
|
||||
|
||||
$this->assertEquals(2, count($allRows), 'There should be 2 rows');
|
||||
$this->assertEquals(5, count($allRows[0]), 'There should be 5 cells in the first row');
|
||||
$this->assertEquals(3, count($allRows[1]), 'There should be only 3 cells in the second row, because empty rows at the end should be skip');
|
||||
|
||||
$expectedRows = [
|
||||
['s1--A1', 's1--B1', 's1--C1', 's1--D1', 's1--E1'],
|
||||
['s1--A2', 's1--B2', 's1--C2'],
|
||||
];
|
||||
$this->assertEquals($expectedRows, $allRows);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testReadShouldSkipEmptyCellsAtTheEndIfDimensionsNotSpecified()
|
||||
{
|
||||
$allRows = $this->getAllRowsForFile('sheet_without_dimensions_and_empty_cells.xlsx');
|
||||
|
||||
$this->assertEquals(2, count($allRows), 'There should be 2 rows');
|
||||
$this->assertEquals(5, count($allRows[0]), 'There should be 5 cells in the first row');
|
||||
$this->assertEquals(3, count($allRows[1]), 'There should be only 3 cells in the second row, because empty rows at the end should be skip');
|
||||
|
||||
$expectedRows = [
|
||||
['s1--A1', 's1--B1', 's1--C1', 's1--D1', 's1--E1'],
|
||||
['s1--A2', 's1--B2', 's1--C2'],
|
||||
];
|
||||
$this->assertEquals($expectedRows, $allRows);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testReadShouldSkipEmptyRows()
|
||||
{
|
||||
$allRows = $this->getAllRowsForFile('sheet_with_empty_rows.xlsx');
|
||||
|
||||
$this->assertEquals(2, count($allRows), 'There should be only 2 rows, because the empty row is skipped');
|
||||
|
||||
$expectedRows = [
|
||||
['s1--A1', 's1--B1', 's1--C1', 's1--D1', 's1--E1'],
|
||||
['s1--A3', 's1--B3', 's1--C3', 's1--D3', 's1--E3'],
|
||||
];
|
||||
$this->assertEquals($expectedRows, $allRows);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testReadShouldSkipPronunciationData()
|
||||
{
|
||||
$allRows = $this->getAllRowsForFile('sheet_with_pronunciation.xlsx');
|
||||
|
||||
$expectedRow = ['名前', '一二三四'];
|
||||
$this->assertEquals($expectedRow, $allRows[0], 'Pronunciation data should be removed.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testReadShouldBeProtectedAgainstBillionLaughsAttack()
|
||||
{
|
||||
$allRows = $this->getAllRowsForFile('billion_laughs_test_file.xlsx');
|
||||
|
||||
$expectedMaxMemoryUsage = 10 * 1024 * 1024; // 10MB
|
||||
$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'];
|
||||
$this->assertEquals($expectedFirstRow, $allRows[0], 'Entities should be ignored when reading XML files.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fileName
|
||||
* @return array All the read rows the given file
|
||||
*/
|
||||
private function getAllRowsForFile($fileName)
|
||||
{
|
||||
$allRows = [];
|
||||
$resourcePath = $this->getResourcePath($fileName);
|
||||
|
||||
$reader = ReaderFactory::create(Type::XLSX);
|
||||
$reader->open($resourcePath);
|
||||
|
||||
while ($reader->hasNextSheet()) {
|
||||
$reader->nextSheet();
|
||||
|
||||
while ($reader->hasNextRow()) {
|
||||
$allRows[] = $reader->nextRow();
|
||||
}
|
||||
}
|
||||
|
||||
$reader->close();
|
||||
|
||||
return $allRows;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Utility class for making PHP reflection easier to use.
|
||||
*/
|
||||
class ReflectionHelper
|
||||
{
|
||||
private static $privateVarsToReset = array();
|
||||
|
||||
/**
|
||||
* Resets any static vars that were set to their
|
||||
* original values (to not screw up later unit test runs).
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function reset()
|
||||
{
|
||||
foreach (self::$privateVarsToReset as $class => $valueNames) {
|
||||
foreach ($valueNames as $valueName => $originalValue) {
|
||||
self::setStaticValue($class, $valueName, $originalValue, $saveOriginalValue = false);
|
||||
}
|
||||
}
|
||||
self::$privateVarsToReset = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of a static private or public class property.
|
||||
* Used to test internals of class without having to make the property public
|
||||
*
|
||||
* @param string $class
|
||||
* @param string $valueName
|
||||
* @return mixed|null
|
||||
*/
|
||||
public static function getStaticValue($class, $valueName)
|
||||
{
|
||||
$reflectionClass = new ReflectionClass($class);
|
||||
$reflectionProperty = $reflectionClass->getProperty($valueName);
|
||||
$reflectionProperty->setAccessible(true);
|
||||
$value = $reflectionProperty->getValue();
|
||||
|
||||
// clean up
|
||||
$reflectionProperty->setAccessible(false);
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of a static private or public class property.
|
||||
* Used to test internals of class without having to make the property public
|
||||
*
|
||||
* @param string $class
|
||||
* @param string $valueName
|
||||
* @param mixed|null $value
|
||||
* @param bool|void $saveOriginalValue
|
||||
* @return void
|
||||
*/
|
||||
public static function setStaticValue($class, $valueName, $value, $saveOriginalValue = true)
|
||||
{
|
||||
$reflectionClass = new ReflectionClass($class);
|
||||
$reflectionProperty = $reflectionClass->getProperty($valueName);
|
||||
$reflectionProperty->setAccessible(true);
|
||||
|
||||
// to prevent side-effects in later tests, we need to remember the original value and reset it on tear down
|
||||
// @NOTE: we need to check isset in case the original value was null or array()
|
||||
if ($saveOriginalValue && (!isset(self::$privateVarsToReset[$class]) || !isset(self::$privateVarsToReset[$class][$name]))) {
|
||||
self::$privateVarsToReset[$class][$valueName] = $reflectionProperty->getValue();
|
||||
}
|
||||
$reflectionProperty->setValue($value);
|
||||
|
||||
// clean up
|
||||
$reflectionProperty->setAccessible(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object $object
|
||||
* @param string $valueName
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
public static function getValueOnObject($object, $valueName)
|
||||
{
|
||||
$reflectionObject = new ReflectionObject($object);
|
||||
$reflectionProperty = $reflectionObject->getProperty($valueName);
|
||||
$reflectionProperty->setAccessible(true);
|
||||
$value = $reflectionProperty->getValue($object);
|
||||
|
||||
// clean up
|
||||
$reflectionProperty->setAccessible(false);
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace Box\Spout;
|
||||
|
||||
/**
|
||||
* Trait TestUsingResource
|
||||
*
|
||||
* @package Box\Spout
|
||||
*/
|
||||
trait TestUsingResource {
|
||||
|
||||
/** @var string Path to the test resources folder */
|
||||
private $resourcesPath = 'tests/resources/';
|
||||
|
||||
/** @var string Path to the test generated resources folder */
|
||||
private $generatedResourcesPath = 'tests/resources/generated/';
|
||||
|
||||
/** @var string Path to the test resources folder, that does not have writing permissions */
|
||||
private $generatedUnwritableResourcesPath = 'tests/resources/generated/unwritable/';
|
||||
|
||||
/**
|
||||
* @param string $resourceName
|
||||
* @return string|null Path of the resource who matches the given name or null if resource not found
|
||||
*/
|
||||
protected function getResourcePath($resourceName)
|
||||
{
|
||||
$resourceType = pathinfo($resourceName, PATHINFO_EXTENSION);
|
||||
$resourcePath = $this->resourcesPath . strtolower($resourceType) . '/' . $resourceName;
|
||||
|
||||
return (file_exists($resourcePath) ? $resourcePath : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $resourceName
|
||||
* @return string Path of the generated resource for the given name
|
||||
*/
|
||||
protected function getGeneratedResourcePath($resourceName)
|
||||
{
|
||||
$resourceType = pathinfo($resourceName, PATHINFO_EXTENSION);
|
||||
$generatedResourcePath = $this->generatedResourcesPath . strtolower($resourceType) . '/' . $resourceName;
|
||||
|
||||
return $generatedResourcePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $resourceName
|
||||
* @return void
|
||||
*/
|
||||
protected function createGeneratedFolderIfNeeded($resourceName)
|
||||
{
|
||||
$resourceType = pathinfo($resourceName, PATHINFO_EXTENSION);
|
||||
$generatedResourcePathForType = $this->generatedResourcesPath . strtolower($resourceType);
|
||||
|
||||
if (!file_exists($generatedResourcePathForType)) {
|
||||
mkdir($generatedResourcePathForType, 0777, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $resourceName
|
||||
* @return string Path of the generated unwritable (because parent folder is read only) resource for the given name
|
||||
*/
|
||||
protected function getGeneratedUnwritableResourcePath($resourceName)
|
||||
{
|
||||
return $this->generatedUnwritableResourcesPath . $resourceName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function createUnwritableFolderIfNeeded()
|
||||
{
|
||||
if (!file_exists($this->generatedUnwritableResourcesPath)) {
|
||||
// Make sure generated folder exists first
|
||||
if (!file_exists($this->generatedResourcesPath)) {
|
||||
mkdir($this->generatedResourcesPath, 0777, true);
|
||||
}
|
||||
|
||||
// 0444 = read only
|
||||
mkdir($this->generatedUnwritableResourcesPath, 0444, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
namespace Box\Spout\Writer;
|
||||
|
||||
use Box\Spout\Common\Type;
|
||||
use Box\Spout\TestUsingResource;
|
||||
|
||||
/**
|
||||
* Class CSVTest
|
||||
*
|
||||
* @package Box\Spout\Writer
|
||||
*/
|
||||
class CSVTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
use TestUsingResource;
|
||||
|
||||
/**
|
||||
* @expectedException \Box\Spout\Common\Exception\IOException
|
||||
*/
|
||||
public function testWriteShouldThrowExceptionIfCannotOpenFileForWriting()
|
||||
{
|
||||
$fileName = 'file_that_wont_be_written.csv';
|
||||
$this->createUnwritableFolderIfNeeded($fileName);
|
||||
$filePath = $this->getGeneratedUnwritableResourcePath($fileName);
|
||||
|
||||
$writer = WriterFactory::create(Type::CSV);
|
||||
@$writer->openToFile($filePath);
|
||||
$writer->addRow(['csv--11', 'csv--12']);
|
||||
$writer->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Box\Spout\Writer\Exception\WriterNotOpenedException
|
||||
*/
|
||||
public function testWriteShouldThrowExceptionIfCallAddRowBeforeOpeningWriter()
|
||||
{
|
||||
$writer = WriterFactory::create(Type::CSV);
|
||||
$writer->addRow(['csv--11', 'csv--12']);
|
||||
$writer->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Box\Spout\Writer\Exception\WriterNotOpenedException
|
||||
*/
|
||||
public function testWriteShouldThrowExceptionIfCallAddRowsBeforeOpeningWriter()
|
||||
{
|
||||
$writer = WriterFactory::create(Type::CSV);
|
||||
$writer->addRows([['csv--11', 'csv--12']]);
|
||||
$writer->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testWriteShouldAddUtf8Bom()
|
||||
{
|
||||
$allRows = [
|
||||
['csv--11', 'csv--12'],
|
||||
];
|
||||
$writtenContent = $this->writeToCsvFileAndReturnWrittenContent($allRows, 'csv_with_utf8_bom.csv');
|
||||
|
||||
$this->assertContains(CSV::UTF8_BOM, $writtenContent, 'The CSV file should contain a UTF-8 BOM');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testWriteShouldSupportNullValues()
|
||||
{
|
||||
$allRows = [
|
||||
['csv--11', null, 'csv--13'],
|
||||
];
|
||||
$writtenContent = $this->writeToCsvFileAndReturnWrittenContent($allRows, 'csv_with_null_values.csv');
|
||||
$writtenContent = $this->trimWrittenContent($writtenContent);
|
||||
|
||||
$this->assertEquals('csv--11,,csv--13', $writtenContent, 'The null values should be replaced by empty values');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testWriteShouldSupportCustomFieldDelimiter()
|
||||
{
|
||||
$allRows = [
|
||||
['csv--11', 'csv--12', 'csv--13'],
|
||||
['csv--21', 'csv--22', 'csv--23'],
|
||||
];
|
||||
$writtenContent = $this->writeToCsvFileAndReturnWrittenContent($allRows, 'csv_with_pipe_delimiters.csv', '|');
|
||||
$writtenContent = $this->trimWrittenContent($writtenContent);
|
||||
|
||||
$this->assertEquals("csv--11|csv--12|csv--13\ncsv--21|csv--22|csv--23", $writtenContent, 'The fields should be delimited with |');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testWriteShouldSupportCustomFieldEnclosure()
|
||||
{
|
||||
$allRows = [
|
||||
['This is, a comma', 'csv--12', 'csv--13'],
|
||||
];
|
||||
$writtenContent = $this->writeToCsvFileAndReturnWrittenContent($allRows, 'csv_with_pound_enclosures.csv', ',', '#');
|
||||
$writtenContent = $this->trimWrittenContent($writtenContent);
|
||||
|
||||
$this->assertEquals('#This is, a comma#,csv--12,csv--13', $writtenContent, 'The fields should be enclosed with #');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $allRows
|
||||
* @param string $fileName
|
||||
* @param string $fieldDelimiter
|
||||
* @param string $fieldEnclosure
|
||||
* @return null|string
|
||||
*/
|
||||
private function writeToCsvFileAndReturnWrittenContent($allRows, $fileName, $fieldDelimiter = ',', $fieldEnclosure = '"')
|
||||
{
|
||||
$this->createGeneratedFolderIfNeeded($fileName);
|
||||
$resourcePath = $this->getGeneratedResourcePath($fileName);
|
||||
|
||||
$writer = WriterFactory::create(Type::CSV);
|
||||
$writer->setFieldDelimiter($fieldDelimiter);
|
||||
$writer->setFieldEnclosure($fieldEnclosure);
|
||||
|
||||
$writer->openToFile($resourcePath);
|
||||
$writer->addRows($allRows);
|
||||
$writer->close();
|
||||
|
||||
return file_get_contents($resourcePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $writtenContent
|
||||
* @return string
|
||||
*/
|
||||
private function trimWrittenContent($writtenContent)
|
||||
{
|
||||
// remove line feeds and UTF-8 BOM
|
||||
return trim($writtenContent, "\n" . CSV::UTF8_BOM);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace Box\Spout\Writer\Helper\XLSX;
|
||||
|
||||
/**
|
||||
* Class CellHelperTest
|
||||
*
|
||||
* @package Box\Spout\Writer\Helper\XLSX
|
||||
*/
|
||||
class CellHelperTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function dataProviderForTestGetCellIndexFromColumnIndex()
|
||||
{
|
||||
return [
|
||||
[0, 'A'],
|
||||
[1, 'B'],
|
||||
[25, 'Z'],
|
||||
[26, 'AA'],
|
||||
[28, 'AC'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataProviderForTestGetCellIndexFromColumnIndex
|
||||
*
|
||||
* @param int $columnIndex
|
||||
* @param string $expectedCellIndex
|
||||
* @return void
|
||||
*/
|
||||
public function testGetCellIndexFromColumnIndex($columnIndex, $expectedCellIndex)
|
||||
{
|
||||
$this->assertEquals($expectedCellIndex, CellHelper::getCellIndexFromColumnIndex($columnIndex));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Box\Spout\Writer;
|
||||
|
||||
use Box\Spout\Common\Type;
|
||||
use Box\Spout\TestUsingResource;
|
||||
|
||||
/**
|
||||
* Class SheetTest
|
||||
*
|
||||
* @package Box\Spout\Writer
|
||||
*/
|
||||
class SheetTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
use TestUsingResource;
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testGetSheetNumber()
|
||||
{
|
||||
$sheets = $this->writeDataAndReturnSheets('test_get_sheet_number.xlsx');
|
||||
|
||||
$this->assertEquals(2, count($sheets), '2 sheets should have been created');
|
||||
$this->assertEquals(0, $sheets[0]->getSheetNumber(), 'The first sheet should be number 0');
|
||||
$this->assertEquals(1, $sheets[1]->getSheetNumber(), 'The second sheet should be number 1');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testGetSheetName()
|
||||
{
|
||||
$sheets = $this->writeDataAndReturnSheets('test_get_sheet_name.xlsx');
|
||||
|
||||
$this->assertEquals(2, count($sheets), '2 sheets should have been created');
|
||||
$this->assertEquals('Sheet1', $sheets[0]->getName(), 'Invalid name for the first sheet');
|
||||
$this->assertEquals('Sheet2', $sheets[1]->getName(), 'Invalid name for the second sheet');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fileName
|
||||
* @return Sheet[]
|
||||
*/
|
||||
private function writeDataAndReturnSheets($fileName)
|
||||
{
|
||||
$this->createGeneratedFolderIfNeeded($fileName);
|
||||
$resourcePath = $this->getGeneratedResourcePath($fileName);
|
||||
|
||||
/** @var \Box\Spout\Writer\XLSX $writer */
|
||||
$writer = WriterFactory::create(Type::XLSX);
|
||||
$writer->openToFile($resourcePath);
|
||||
|
||||
$writer->addRow(['xlsx--sheet1--11', 'xlsx--sheet1--12']);
|
||||
$writer->addNewSheetAndMakeItCurrent();
|
||||
$writer->addRow(['xlsx--sheet2--11', 'xlsx--sheet2--12', 'xlsx--sheet2--13']);
|
||||
|
||||
$writer->close();
|
||||
|
||||
return $writer->getSheets();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,417 @@
|
||||
<?php
|
||||
|
||||
namespace Box\Spout\Writer;
|
||||
|
||||
use Box\Spout\Common\Type;
|
||||
use Box\Spout\TestUsingResource;
|
||||
|
||||
/**
|
||||
* Class XLSXTest
|
||||
*
|
||||
* @package Box\Spout\Writer
|
||||
*/
|
||||
class XLSXTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
use TestUsingResource;
|
||||
|
||||
/**
|
||||
* @expectedException \Box\Spout\Common\Exception\IOException
|
||||
*/
|
||||
public function testAddRowShouldThrowExceptionIfCannotOpenAFileForWriting()
|
||||
{
|
||||
$fileName = 'file_that_wont_be_written.xlsx';
|
||||
$this->createUnwritableFolderIfNeeded($fileName);
|
||||
$filePath = $this->getGeneratedUnwritableResourcePath($fileName);
|
||||
|
||||
$writer = WriterFactory::create(Type::XLSX);
|
||||
@$writer->openToFile($filePath);
|
||||
$writer->addRow(['xlsx--11', 'xlsx--12']);
|
||||
$writer->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Box\Spout\Writer\Exception\WriterNotOpenedException
|
||||
*/
|
||||
public function testAddRowShouldThrowExceptionIfCallAddRowBeforeOpeningWriter()
|
||||
{
|
||||
$writer = WriterFactory::create(Type::XLSX);
|
||||
$writer->addRow(['xlsx--11', 'xlsx--12']);
|
||||
$writer->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Box\Spout\Writer\Exception\WriterNotOpenedException
|
||||
*/
|
||||
public function testAddRowShouldThrowExceptionIfCallAddRowsBeforeOpeningWriter()
|
||||
{
|
||||
$writer = WriterFactory::create(Type::XLSX);
|
||||
$writer->addRows([['xlsx--11', 'xlsx--12']]);
|
||||
$writer->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testAddNewSheetAndMakeItCurrent()
|
||||
{
|
||||
$fileName = 'test_add_new_sheet_and_make_it_current.xlsx';
|
||||
$this->createGeneratedFolderIfNeeded($fileName);
|
||||
$resourcePath = $this->getGeneratedResourcePath($fileName);
|
||||
|
||||
$writer = WriterFactory::create(Type::XLSX);
|
||||
$writer->openToFile($resourcePath);
|
||||
$writer->addNewSheetAndMakeItCurrent();
|
||||
$writer->close();
|
||||
|
||||
$sheets = $writer->getSheets();
|
||||
$this->assertEquals(2, count($sheets), 'There should be 2 sheets');
|
||||
$this->assertEquals($sheets[1], $writer->getCurrentSheet(), 'The current sheet should be the second one.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testSetCurrentSheet()
|
||||
{
|
||||
$fileName = 'test_set_current_sheet.xlsx';
|
||||
$this->createGeneratedFolderIfNeeded($fileName);
|
||||
$resourcePath = $this->getGeneratedResourcePath($fileName);
|
||||
|
||||
$writer = WriterFactory::create(Type::XLSX);
|
||||
$writer->openToFile($resourcePath);
|
||||
|
||||
$writer->addNewSheetAndMakeItCurrent();
|
||||
$writer->addNewSheetAndMakeItCurrent();
|
||||
|
||||
$firstSheet = $writer->getSheets()[0];
|
||||
$writer->setCurrentSheet($firstSheet);
|
||||
|
||||
$writer->close();
|
||||
|
||||
$this->assertEquals($firstSheet, $writer->getCurrentSheet(), 'The current sheet should be the first one.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testAddRowShouldWriteGivenDataToSheetUsingInlineStrings()
|
||||
{
|
||||
$fileName = 'test_add_row_should_write_given_data_to_sheet.xlsx';
|
||||
$dataRows = [
|
||||
['xlsx--11', 'xlsx--12'],
|
||||
['xlsx--21', 'xlsx--22', 'xlsx--23'],
|
||||
];
|
||||
|
||||
$this->writeToXLSXFile($dataRows, $fileName, $shouldUseInlineStrings = true);
|
||||
|
||||
foreach ($dataRows as $dataRow) {
|
||||
foreach ($dataRow as $cellValue) {
|
||||
$this->assertInlineStringWasWrittenToSheet($fileName, 1, $cellValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testAddRowShouldWriteGivenDataToTwoSheetUsingInlineStrings()
|
||||
{
|
||||
$fileName = 'test_add_row_should_write_given_data_to_sheet.xlsx';
|
||||
$dataRows = [
|
||||
['xlsx--11', 'xlsx--12'],
|
||||
['xlsx--21', 'xlsx--22', 'xlsx--23'],
|
||||
];
|
||||
|
||||
$numSheets = 2;
|
||||
$this->writeToMultipleSheetsInXLSXFile($dataRows, $numSheets, $fileName, $shouldUseInlineStrings = true);
|
||||
|
||||
for ($i = 1; $i <= $numSheets; $i++) {
|
||||
foreach ($dataRows as $dataRow) {
|
||||
foreach ($dataRow as $cellValue) {
|
||||
$this->assertInlineStringWasWrittenToSheet($fileName, $numSheets, $cellValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testAddRowShouldWriteGivenDataToSheetUsingSharedStrings()
|
||||
{
|
||||
$fileName = 'test_add_row_should_write_given_data_to_sheet.xlsx';
|
||||
$dataRows = [
|
||||
['xlsx--11', 'xlsx--12'],
|
||||
['xlsx--21', 'xlsx--22', 'xlsx--23'],
|
||||
];
|
||||
|
||||
$this->writeToXLSXFile($dataRows, $fileName, $shouldUseInlineStrings = false);
|
||||
|
||||
foreach ($dataRows as $dataRow) {
|
||||
foreach ($dataRow as $cellValue) {
|
||||
$this->assertSharedStringWasWritten($fileName, $cellValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testAddRowShouldWriteGivenDataToTwoSheetUsingSharedStrings()
|
||||
{
|
||||
$fileName = 'test_add_row_should_write_given_data_to_two_sheet_using_shared_strings.xlsx';
|
||||
$dataRows = [
|
||||
['xlsx--11', 'xlsx--12'],
|
||||
['xlsx--21', 'xlsx--22', 'xlsx--23'],
|
||||
];
|
||||
|
||||
$numSheets = 2;
|
||||
$this->writeToMultipleSheetsInXLSXFile($dataRows, $numSheets, $fileName, $shouldUseInlineStrings = false);
|
||||
|
||||
for ($i = 1; $i <= $numSheets; $i++) {
|
||||
foreach ($dataRows as $dataRow) {
|
||||
foreach ($dataRow as $cellValue) {
|
||||
$this->assertSharedStringWasWritten($fileName, $cellValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testAddRowShouldWriteGivenDataToTheCorrectSheet()
|
||||
{
|
||||
$fileName = 'test_add_row_should_write_given_data_to_the_correct_sheet.xlsx';
|
||||
$dataRowsSheet1 = [
|
||||
['xlsx--sheet1--11', 'xlsx--sheet1--12'],
|
||||
['xlsx--sheet1--21', 'xlsx--sheet1--22', 'xlsx--sheet1--23'],
|
||||
];
|
||||
$dataRowsSheet2 = [
|
||||
['xlsx--sheet2--11', 'xlsx--sheet2--12'],
|
||||
['xlsx--sheet2--21', 'xlsx--sheet2--22', 'xlsx--sheet2--23'],
|
||||
];
|
||||
$dataRowsSheet1Again = [
|
||||
['xlsx--sheet1--31', 'xlsx--sheet1--32'],
|
||||
['xlsx--sheet1--41', 'xlsx--sheet1--42', 'xlsx--sheet1--43'],
|
||||
];
|
||||
|
||||
$this->createGeneratedFolderIfNeeded($fileName);
|
||||
$resourcePath = $this->getGeneratedResourcePath($fileName);
|
||||
|
||||
/** @var \Box\Spout\Writer\XLSX $writer */
|
||||
$writer = WriterFactory::create(Type::XLSX);
|
||||
$writer->setShouldUseInlineStrings(true);
|
||||
|
||||
$writer->openToFile($resourcePath);
|
||||
|
||||
$writer->addRows($dataRowsSheet1);
|
||||
|
||||
$writer->addNewSheetAndMakeItCurrent();
|
||||
$writer->addRows($dataRowsSheet2);
|
||||
|
||||
$firstSheet = $writer->getSheets()[0];
|
||||
$writer->setCurrentSheet($firstSheet);
|
||||
|
||||
$writer->addRows($dataRowsSheet1Again);
|
||||
|
||||
$writer->close();
|
||||
|
||||
foreach ($dataRowsSheet1 as $dataRow) {
|
||||
foreach ($dataRow as $cellValue) {
|
||||
$this->assertInlineStringWasWrittenToSheet($fileName, 1, $cellValue, 'Data should have been written in Sheet 1');
|
||||
}
|
||||
}
|
||||
foreach ($dataRowsSheet2 as $dataRow) {
|
||||
foreach ($dataRow as $cellValue) {
|
||||
$this->assertInlineStringWasWrittenToSheet($fileName, 2, $cellValue, 'Data should have been written in Sheet 2');
|
||||
}
|
||||
}
|
||||
foreach ($dataRowsSheet1Again as $dataRow) {
|
||||
foreach ($dataRow as $cellValue) {
|
||||
$this->assertInlineStringWasWrittenToSheet($fileName, 1, $cellValue, 'Data should have been written in Sheet 1');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testAddRowShouldAutomaticallyCreateNewSheetsIfMaxRowsReachedAndOptionTurnedOn()
|
||||
{
|
||||
$fileName = 'test_add_row_should_automatically_create_new_sheets_if_max_rows_reached_and_option_turned_on.xlsx';
|
||||
$dataRows = [
|
||||
['xlsx--sheet1--11', 'xlsx--sheet1--12'],
|
||||
['xlsx--sheet1--21', 'xlsx--sheet1--22', 'xlsx--sheet1--23'],
|
||||
['xlsx--sheet2--11', 'xlsx--sheet2--12'], // this should be written in a new sheet
|
||||
];
|
||||
|
||||
// set the maxRowsPerSheet limit to 2
|
||||
\ReflectionHelper::setStaticValue('\Box\Spout\Writer\Internal\XLSX\Workbook', 'maxRowsPerWorksheet', 2);
|
||||
|
||||
$writer = $this->writeToXLSXFile($dataRows, $fileName, true, $shouldCreateSheetsAutomatically = true);
|
||||
$this->assertEquals(2, count($writer->getSheets()), '2 sheets should have been created.');
|
||||
|
||||
$this->assertInlineStringWasNotWrittenToSheet($fileName, 1, 'xlsx--sheet2--11');
|
||||
$this->assertInlineStringWasWrittenToSheet($fileName, 2, 'xlsx--sheet2--11');
|
||||
|
||||
\ReflectionHelper::reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testAddRowShouldNotCreateNewSheetsIfMaxRowsReachedAndOptionTurnedOff()
|
||||
{
|
||||
$fileName = 'test_add_row_should_not_create_new_sheets_if_max_rows_reached_and_option_turned_off.xlsx';
|
||||
$dataRows = [
|
||||
['xlsx--sheet1--11', 'xlsx--sheet1--12'],
|
||||
['xlsx--sheet1--21', 'xlsx--sheet1--22', 'xlsx--sheet1--23'],
|
||||
['xlsx--sheet1--31', 'xlsx--sheet1--32'], // this should NOT be written in a new sheet
|
||||
];
|
||||
|
||||
// set the maxRowsPerSheet limit to 2
|
||||
\ReflectionHelper::setStaticValue('\Box\Spout\Writer\Internal\XLSX\Workbook', 'maxRowsPerWorksheet', 2);
|
||||
|
||||
$writer = $this->writeToXLSXFile($dataRows, $fileName, true, $shouldCreateSheetsAutomatically = false);
|
||||
$this->assertEquals(1, count($writer->getSheets()), 'Only 1 sheet should have been created.');
|
||||
|
||||
$this->assertInlineStringWasNotWrittenToSheet($fileName, 1, 'xlsx--sheet1--31');
|
||||
|
||||
\ReflectionHelper::reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testAddRowShouldEscapeHtmlSpecialCharacters()
|
||||
{
|
||||
$fileName = 'test_add_row_should_escape_html_special_characters.xlsx';
|
||||
$dataRows = [
|
||||
['I\'m in "great" mood', 'This <must> be escaped & tested'],
|
||||
];
|
||||
|
||||
$this->writeToXLSXFile($dataRows, $fileName);
|
||||
|
||||
$this->assertInlineStringWasWrittenToSheet($fileName, 1, 'I'm in "great" mood', 'Quotes should be escaped');
|
||||
$this->assertInlineStringWasWrittenToSheet($fileName, 1, 'This <must> be escaped & tested', '<, > and & should be escaped');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testAddRowShouldEscapeControlCharacters()
|
||||
{
|
||||
$fileName = 'test_add_row_should_escape_html_special_characters.xlsx';
|
||||
$dataRows = [
|
||||
['control\'s '.chr(21).' "character"'],
|
||||
];
|
||||
|
||||
$this->writeToXLSXFile($dataRows, $fileName);
|
||||
|
||||
$this->assertInlineStringWasWrittenToSheet($fileName, 1, 'control's _x0015_ "character"');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param array $allRows
|
||||
* @param string $fileName
|
||||
* @param bool $shouldUseInlineStrings
|
||||
* @param bool $shouldCreateSheetsAutomatically
|
||||
* @return XLSX
|
||||
*/
|
||||
private function writeToXLSXFile($allRows, $fileName, $shouldUseInlineStrings = true, $shouldCreateSheetsAutomatically = true)
|
||||
{
|
||||
$this->createGeneratedFolderIfNeeded($fileName);
|
||||
$resourcePath = $this->getGeneratedResourcePath($fileName);
|
||||
|
||||
/** @var \Box\Spout\Writer\XLSX $writer */
|
||||
$writer = WriterFactory::create(Type::XLSX);
|
||||
$writer->setShouldUseInlineStrings($shouldUseInlineStrings);
|
||||
$writer->setShouldCreateNewSheetsAutomatically($shouldCreateSheetsAutomatically);
|
||||
|
||||
$writer->openToFile($resourcePath);
|
||||
$writer->addRows($allRows);
|
||||
$writer->close();
|
||||
|
||||
return $writer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $allRows
|
||||
* @param int $numSheets
|
||||
* @param string $fileName
|
||||
* @param bool $shouldUseInlineStrings
|
||||
* @param bool $shouldCreateSheetsAutomatically
|
||||
* @return XLSX
|
||||
*/
|
||||
private function writeToMultipleSheetsInXLSXFile($allRows, $numSheets, $fileName, $shouldUseInlineStrings = true, $shouldCreateSheetsAutomatically = true)
|
||||
{
|
||||
$this->createGeneratedFolderIfNeeded($fileName);
|
||||
$resourcePath = $this->getGeneratedResourcePath($fileName);
|
||||
|
||||
/** @var \Box\Spout\Writer\XLSX $writer */
|
||||
$writer = WriterFactory::create(Type::XLSX);
|
||||
$writer->setShouldUseInlineStrings($shouldUseInlineStrings);
|
||||
$writer->setShouldCreateNewSheetsAutomatically($shouldCreateSheetsAutomatically);
|
||||
|
||||
$writer->openToFile($resourcePath);
|
||||
$writer->addRows($allRows);
|
||||
|
||||
for ($i = 1; $i < $numSheets; $i++) {
|
||||
$writer->addNewSheetAndMakeItCurrent();
|
||||
$writer->addRows($allRows);
|
||||
}
|
||||
|
||||
$writer->close();
|
||||
|
||||
return $writer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fileName
|
||||
* @param int $sheetNumber
|
||||
* @param string $inlineString
|
||||
* @param string $message
|
||||
* @return void
|
||||
*/
|
||||
private function assertInlineStringWasWrittenToSheet($fileName, $sheetNumber, $inlineString, $message = '')
|
||||
{
|
||||
$resourcePath = $this->getGeneratedResourcePath($fileName);
|
||||
$pathToSheetFile = $resourcePath . '#xl/worksheets/sheet' . $sheetNumber . '.xml';
|
||||
$xmlContents = file_get_contents('zip://' . $pathToSheetFile);
|
||||
|
||||
$this->assertContains($inlineString, $xmlContents, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fileName
|
||||
* @param int $sheetNumber
|
||||
* @param string $inlineString
|
||||
* @param string $message
|
||||
* @return void
|
||||
*/
|
||||
private function assertInlineStringWasNotWrittenToSheet($fileName, $sheetNumber, $inlineString, $message = '')
|
||||
{
|
||||
$resourcePath = $this->getGeneratedResourcePath($fileName);
|
||||
$pathToSheetFile = $resourcePath . '#xl/worksheets/sheet' . $sheetNumber . '.xml';
|
||||
$xmlContents = file_get_contents('zip://' . $pathToSheetFile);
|
||||
|
||||
$this->assertNotContains($inlineString, $xmlContents, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fileName
|
||||
* @param string $sharedString
|
||||
* @param string $message
|
||||
* @return void
|
||||
*/
|
||||
private function assertSharedStringWasWritten($fileName, $sharedString, $message = '')
|
||||
{
|
||||
$resourcePath = $this->getGeneratedResourcePath($fileName);
|
||||
$pathToSharedStringsFile = $resourcePath . '#xl/sharedStrings.xml';
|
||||
$xmlContents = file_get_contents('zip://' . $pathToSharedStringsFile);
|
||||
|
||||
$this->assertContains($sharedString, $xmlContents, $message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
require_once(dirname(__DIR__) . '/vendor/autoload.php');
|
||||
|
||||
require_once(dirname(__DIR__) . '/tests/Spout/TestUsingResource.php');
|
||||
require_once(dirname(__DIR__) . '/tests/Spout/ReflectionHelper.php');
|
||||
@@ -0,0 +1,3 @@
|
||||
csv--11|csv--12|csv--13
|
||||
csv--21|csv--22|csv--23
|
||||
csv--31|csv--32|csv--33
|
||||
|
@@ -0,0 +1,3 @@
|
||||
csv--11,csv--12,csv--13
|
||||
csv--21,csv--22,csv--23
|
||||
csv--31,csv--32,csv--33
|
||||
|
@@ -0,0 +1,2 @@
|
||||
#This is, a comma#,csv--12,csv--13
|
||||
csv--21,csv--22,csv--23
|
||||
|
@@ -0,0 +1,2 @@
|
||||
"This is, a comma",csv--12
|
||||
csv--21,csv--22
|
||||
|
@@ -0,0 +1,3 @@
|
||||
csv--11,csv--12,csv--13
|
||||
csv--21,csv--22
|
||||
csv--31
|
||||
|
@@ -0,0 +1,3 @@
|
||||
csv--11,csv--12,csv--13
|
||||
csv--21,,csv--23
|
||||
csv--31,csv--32,
|
||||
|
@@ -0,0 +1,3 @@
|
||||
csv--11,csv--12,csv--13
|
||||
|
||||
csv--31,csv--32,csv--33
|
||||
|
@@ -0,0 +1,2 @@
|
||||
csv--11,csv--12,csv--13
|
||||
csv--21,csv--22,csv--23
|
||||
|
Binary file not shown.
@@ -0,0 +1,3 @@
|
||||
csv--11,csv--12,csv--13
|
||||
csv--21,csv--22,csv--23
|
||||
csv--31,csv--32,csv--33
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in new issue
Block a user