Instead of the hasNext() / next() syntax, readers now implements the PHP iterator pattern. It allows readers to be used with a foreach() loop. All readers now share the same structure (CSV is treated as having exactly one sheet): - one concrete Reader - one SheetIterator, exposed by the Reader - one or more Sheets, returned at every iteration - one RowIterator, exposed by the Sheet Introducing the concept of sheets for CSV may be kind of confusing but it makes Spout way more consistent. Also, this confusion may be resolved by creating a wrapper around the readers if needed. -- This commit does not delete the old files, not change the folder structure for Writers. This will be done in another commit.
182 lines
4.8 KiB
PHP
182 lines
4.8 KiB
PHP
<?php
|
|
|
|
namespace Box\Spout\Reader\CSV;
|
|
|
|
use Box\Spout\Common\Type;
|
|
use Box\Spout\Reader\ReaderFactory2;
|
|
use Box\Spout\TestUsingResource;
|
|
|
|
/**
|
|
* Class ReaderTest
|
|
*
|
|
* @package Box\Spout\Reader\CSV
|
|
*/
|
|
class ReaderTest extends \PHPUnit_Framework_TestCase
|
|
{
|
|
use TestUsingResource;
|
|
|
|
/**
|
|
* @expectedException \Box\Spout\Common\Exception\IOException
|
|
*
|
|
* @return void
|
|
*/
|
|
public function testOpenShouldThrowExceptionIfFileDoesNotExist()
|
|
{
|
|
ReaderFactory2::create(Type::CSV)->open('/path/to/fake/file.csv');
|
|
}
|
|
|
|
/**
|
|
* @expectedException \Box\Spout\Common\Exception\IOException
|
|
*
|
|
* @return void
|
|
*/
|
|
public function testOpenShouldThrowExceptionIfFileNotReadable()
|
|
{
|
|
$helperStub = $this->getMockBuilder('\Box\Spout\Common\Helper\GlobalFunctionsHelper')
|
|
->setMethods(['is_readable'])
|
|
->getMock();
|
|
$helperStub->method('is_readable')->willReturn(false);
|
|
|
|
$resourcePath = $this->getResourcePath('csv_standard.csv');
|
|
|
|
$reader = ReaderFactory2::create(Type::CSV);
|
|
$reader->setGlobalFunctionsHelper($helperStub);
|
|
$reader->open($resourcePath);
|
|
}
|
|
|
|
|
|
/**
|
|
* @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 = ReaderFactory2::create(Type::CSV);
|
|
$reader->setFieldDelimiter($fieldDelimiter);
|
|
$reader->setFieldEnclosure($fieldEnclosure);
|
|
|
|
$reader->open($resourcePath);
|
|
|
|
foreach ($reader->getSheetIterator() as $sheetIndex => $sheet) {
|
|
foreach ($sheet->getRowIterator() as $rowIndex => $row) {
|
|
$allRows[] = $row;
|
|
}
|
|
}
|
|
|
|
$reader->close();
|
|
|
|
return $allRows;
|
|
}
|
|
}
|