Compare commits

...
16 Commits
Author SHA1 Message Date
Adrien Loison b69e28050b Merge pull request #187 from skeleton/issue-183
Fix line breaks on CSV reader
2016-03-23 17:02:42 -07:00
skeleton d6e8fe4b54 Fix line breaks on CSV reader 2016-03-23 23:26:49 +01:00
Adrien Loison e321f30c3b Merge pull request #191 from box/close_file_pointer_when_done_writing
Writers did not close written file pointer
2016-03-23 10:45:42 -07:00
Adrien Loison c31373fb1a Writers did not close written file pointer 2016-03-23 10:21:27 -07:00
Adrien Loison 6c57125c0c Merge pull request #189 from madflow/ods-missing-values
Fixes for #184
2016-03-21 10:24:38 -07:00
madflow 30837f869d Coding style and typos 2016-03-20 08:46:30 +01:00
madflow e60054f3c4 More explicit rule for ignoring empty placeholder cells in Excel ODS #184 2016-03-19 11:34:32 +01:00
madflow 3ee7099c95 Fix zeros treated as missing values #184 2016-03-19 11:34:32 +01:00
madflow 2b1160bb33 Tests for #184 2016-03-19 11:34:31 +01:00
Adrien Loison 049fd990b9 Merge pull request #188 from box/stream_wrapper_support
Custom stream wrapper support
2016-03-18 18:09:22 -07:00
Adrien Loison d2ac54c578 Custom stream wrapper support
Added support for custom stream wrappers, such as "fly" or "s3".
Support is determined per reader.
2016-03-18 17:09:13 -07:00
Adrien Loison 0c90d102ef Merge pull request #179 from lichunqiang/gitattributes
Add .gitattributes
2016-02-21 18:11:03 -08:00
lichunqiang e39dcb3847 Add .gitattributes 2016-02-22 09:48:28 +08:00
Adrien Loison 10d1140a95 Merge pull request #175 from IlyaBakhlin/master
Fixing the boolean bug.
2016-02-15 16:43:30 -08:00
Ilya Bakhlin 48debbcbca Simplifying the fix. 2016-02-15 22:11:04 +01:00
Ilya Bakhlin f4d6fb87ee Fixing the boolean bug. 2016-02-15 15:55:46 +01:00
23 changed files with 513 additions and 56 deletions

No files matched your search

+11
View File
@@ -0,0 +1,11 @@
# Ignore all test and documentation for archive
/tests export-ignore
/.editorconfig export-ignore
/.gitattributes export-ignore
/.gitignore export-ignore
/.scrutinizer.yml export-ignore
/.travis.yml export-ignore
/composer.lock export-ignore
/CONTRIBUTING.md export-ignore
/logo.png export-ignore
/phpunit.xml export-ignore
@@ -281,32 +281,14 @@ class GlobalFunctionsHelper
} }
/** /**
* Wrapper around global function stream_get_line() * Wrapper around global function stream_get_wrappers()
* @see stream_get_line() * @see stream_get_wrappers()
* *
* @param resource $handle
* @param int $length
* @param string|void $ending
* @return string|bool
*/
public function stream_get_line($handle, $length, $ending = null)
{
return stream_get_line($handle, $length, $ending);
}
/**
* Wrapper around global function str_getcsv()
* @see str_getcsv()
*
* @param string $input
* @param string|void $delimiter
* @param string|void $enclosure
* @param string|void $escape
* @return array * @return array
*/ */
public function str_getcsv($input, $delimiter = null, $enclosure = null, $escape = null) public function stream_get_wrappers()
{ {
return str_getcsv($input, $delimiter, $enclosure, $escape); return stream_get_wrappers();
} }
/** /**
+75 -3
View File
@@ -19,6 +19,13 @@ abstract class AbstractReader implements ReaderInterface
/** @var \Box\Spout\Common\Helper\GlobalFunctionsHelper Helper to work with global functions */ /** @var \Box\Spout\Common\Helper\GlobalFunctionsHelper Helper to work with global functions */
protected $globalFunctionsHelper; protected $globalFunctionsHelper;
/**
* Returns whether stream wrappers are supported
*
* @return bool
*/
abstract protected function doesSupportStreamWrapper();
/** /**
* Opens the file at the given file path to make it ready to be read * Opens the file at the given file path to make it ready to be read
* *
@@ -62,6 +69,10 @@ abstract class AbstractReader implements ReaderInterface
*/ */
public function open($filePath) public function open($filePath)
{ {
if ($this->isStreamWrapper($filePath) && (!$this->doesSupportStreamWrapper() || !$this->isSupportedStreamWrapper($filePath))) {
throw new IOException("Could not open $filePath for reading! Stream wrapper used is not supported for this type of file.");
}
if (!$this->isPhpStream($filePath)) { if (!$this->isPhpStream($filePath)) {
// we skip the checks if the provided file path points to a PHP stream // we skip the checks if the provided file path points to a PHP stream
if (!$this->globalFunctionsHelper->file_exists($filePath)) { if (!$this->globalFunctionsHelper->file_exists($filePath)) {
@@ -72,8 +83,7 @@ abstract class AbstractReader implements ReaderInterface
} }
try { try {
// Need to use realpath to fix "Can't open file" on some Windows setup $fileRealPath = $this->getFileRealPath($filePath);
$fileRealPath = realpath($filePath);
$this->openReader($fileRealPath); $this->openReader($fileRealPath);
$this->isStreamOpened = true; $this->isStreamOpened = true;
} catch (\Exception $exception) { } catch (\Exception $exception) {
@@ -81,6 +91,67 @@ abstract class AbstractReader implements ReaderInterface
} }
} }
/**
* Returns the real path of the given path.
* If the given path is a valid stream wrapper, returns the path unchanged.
*
* @param string $filePath
* @return string
*/
protected function getFileRealPath($filePath)
{
if ($this->isSupportedStreamWrapper($filePath)) {
return $filePath;
}
// Need to use realpath to fix "Can't open file" on some Windows setup
return realpath($filePath);
}
/**
* Returns the scheme of the custom stream wrapper, if the path indicates a stream wrapper is used.
* For example, php://temp => php, s3://path/to/file => s3...
*
* @param string $filePath Path of the file to be read
* @return string|null The stream wrapper scheme or NULL if not a stream wrapper
*/
protected function getStreamWrapperScheme($filePath)
{
$streamScheme = null;
if (preg_match('/^(\w+):\/\//', $filePath, $matches)) {
$streamScheme = $matches[1];
}
return $streamScheme;
}
/**
* Checks if the given path is an unsupported stream wrapper
* (like local path, php://temp, mystream://foo/bar...).
*
* @param string $filePath Path of the file to be read
* @return bool Whether the given path is an unsupported stream wrapper
*/
protected function isStreamWrapper($filePath)
{
return ($this->getStreamWrapperScheme($filePath) !== null);
}
/**
* Checks if the given path is an supported stream wrapper
* (like php://temp, mystream://foo/bar...).
* If the given path is a local path, returns true.
*
* @param string $filePath Path of the file to be read
* @return bool Whether the given path is an supported stream wrapper
*/
protected function isSupportedStreamWrapper($filePath)
{
$streamScheme = $this->getStreamWrapperScheme($filePath);
return ($streamScheme !== null) ?
in_array($streamScheme, $this->globalFunctionsHelper->stream_get_wrappers()) :
true;
}
/** /**
* Checks if a path is a PHP stream (like php://output, php://memory, ...) * Checks if a path is a PHP stream (like php://output, php://memory, ...)
* *
@@ -89,7 +160,8 @@ abstract class AbstractReader implements ReaderInterface
*/ */
protected function isPhpStream($filePath) protected function isPhpStream($filePath)
{ {
return (strpos($filePath, 'php://') === 0); $streamScheme = $this->getStreamWrapperScheme($filePath);
return ($streamScheme === 'php');
} }
/** /**
+18
View File
@@ -32,6 +32,9 @@ class Reader extends AbstractReader
/** @var string Defines the End of line */ /** @var string Defines the End of line */
protected $endOfLineCharacter = "\n"; protected $endOfLineCharacter = "\n";
/** @var string */
protected $autoDetectLineEndings;
/** /**
* Sets the field delimiter for the CSV. * Sets the field delimiter for the CSV.
* Needs to be called before opening the reader. * Needs to be called before opening the reader.
@@ -84,6 +87,16 @@ class Reader extends AbstractReader
return $this; return $this;
} }
/**
* Returns whether stream wrappers are supported
*
* @return bool
*/
protected function doesSupportStreamWrapper()
{
return true;
}
/** /**
* Opens the file at the given path to make it ready to be read. * Opens the file at the given path to make it ready to be read.
* If setEncoding() was not called, it assumes that the file is encoded in UTF-8. * If setEncoding() was not called, it assumes that the file is encoded in UTF-8.
@@ -94,6 +107,9 @@ class Reader extends AbstractReader
*/ */
protected function openReader($filePath) protected function openReader($filePath)
{ {
$this->autoDetectLineEndings = ini_get('auto_detect_line_endings');
ini_set('auto_detect_line_endings', '1');
$this->filePointer = $this->globalFunctionsHelper->fopen($filePath, 'r'); $this->filePointer = $this->globalFunctionsHelper->fopen($filePath, 'r');
if (!$this->filePointer) { if (!$this->filePointer) {
throw new IOException("Could not open file $filePath for reading."); throw new IOException("Could not open file $filePath for reading.");
@@ -130,5 +146,7 @@ class Reader extends AbstractReader
if ($this->filePointer) { if ($this->filePointer) {
$this->globalFunctionsHelper->fclose($this->filePointer); $this->globalFunctionsHelper->fclose($this->filePointer);
} }
ini_set('auto_detect_line_endings', $this->autoDetectLineEndings);
} }
} }
+32 -21
View File
@@ -14,7 +14,7 @@ use Box\Spout\Common\Helper\EncodingHelper;
class RowIterator implements IteratorInterface class RowIterator implements IteratorInterface
{ {
/** /**
* If no value is given to stream_get_line(), it defaults to 8192 (which may be too low). * If no value is given to fgetcsv(), it defaults to 8192 (which may be too low).
* Alignement with other functions like fgets() is discussed here: https://bugs.php.net/bug.php?id=48421 * Alignement with other functions like fgets() is discussed here: https://bugs.php.net/bug.php?id=48421
*/ */
const MAX_READ_BYTES_PER_LINE = 32768; const MAX_READ_BYTES_PER_LINE = 32768;
@@ -128,16 +128,12 @@ class RowIterator implements IteratorInterface
} }
do { do {
$lineData = false; $rowData = $this->getNextUTF8EncodedRow();
$utf8EncodedLineData = $this->getNextUTF8EncodedLine();
if ($utf8EncodedLineData !== false) {
$lineData = $this->globalFunctionsHelper->str_getcsv($utf8EncodedLineData, $this->fieldDelimiter, $this->fieldEnclosure);
}
$hasNowReachedEndOfFile = $this->globalFunctionsHelper->feof($this->filePointer); $hasNowReachedEndOfFile = $this->globalFunctionsHelper->feof($this->filePointer);
} while (($lineData === false && !$hasNowReachedEndOfFile) || $this->isEmptyLine($lineData)); } while (($rowData === false && !$hasNowReachedEndOfFile) || $this->isEmptyLine($rowData));
if ($lineData !== false) { if ($rowData !== false) {
$this->rowDataBuffer = $lineData; $this->rowDataBuffer = $rowData;
$this->numReadRows++; $this->numReadRows++;
} else { } else {
// If we reach this point, it means end of file was reached. // If we reach this point, it means end of file was reached.
@@ -147,24 +143,39 @@ class RowIterator implements IteratorInterface
} }
/** /**
* Returns the next line, converted if necessary to UTF-8. * Returns the next row, converted if necessary to UTF-8.
* Neither fgets nor fgetcsv don't work with non UTF-8 data... so we need to do some things manually. * As fgetcsv() does not manage correctly encoding for non UTF-8 data,
* we remove manually whitespace with ltrim or rtrim (depending on the order of the bytes)
* *
* @return string|false The next line for the current file pointer, encoded in UTF-8 or FALSE if nothing to read * @return array|false The row for the current file pointer, encoded in UTF-8 or FALSE if nothing to read
* @throws \Box\Spout\Common\Exception\EncodingConversionException If unable to convert data to UTF-8 * @throws \Box\Spout\Common\Exception\EncodingConversionException If unable to convert data to UTF-8
*/ */
protected function getNextUTF8EncodedLine() protected function getNextUTF8EncodedRow()
{ {
// Read until the EOL delimiter or EOF is reached. The delimiter's encoding needs to match the CSV's encoding. $encodedRowData = fgetcsv($this->filePointer, self::MAX_READ_BYTES_PER_LINE, $this->fieldDelimiter, $this->fieldEnclosure);
$encodedEOLDelimiter = $this->getEncodedEOLDelimiter(); if (false === $encodedRowData) {
$encodedLineData = $this->globalFunctionsHelper->stream_get_line($this->filePointer, self::MAX_READ_BYTES_PER_LINE, $encodedEOLDelimiter); return false;
}
// If the line could have been read, it can be converted to UTF-8 foreach ($encodedRowData as $cellIndex => $cellValue) {
$utf8EncodedLineData = ($encodedLineData !== false) ? switch($this->encoding) {
$this->encodingHelper->attemptConversionToUTF8($encodedLineData, $this->encoding) : case EncodingHelper::ENCODING_UTF16_LE:
false; case EncodingHelper::ENCODING_UTF32_LE:
// remove whitespace from the beginning of a string as fgetcsv() add extra whitespace when it try to explode non UTF-8 data
$cellValue = ltrim($cellValue);
break;
return $utf8EncodedLineData; case EncodingHelper::ENCODING_UTF16_BE:
case EncodingHelper::ENCODING_UTF32_BE:
// remove whitespace from the end of a string as fgetcsv() add extra whitespace when it try to explode non UTF-8 data
$cellValue = rtrim($cellValue);
break;
}
$encodedRowData[$cellIndex] = $this->encodingHelper->attemptConversionToUTF8($cellValue, $this->encoding);
}
return $encodedRowData;
} }
/** /**
+10
View File
@@ -19,6 +19,16 @@ class Reader extends AbstractReader
/** @var SheetIterator To iterator over the ODS sheets */ /** @var SheetIterator To iterator over the ODS sheets */
protected $sheetIterator; protected $sheetIterator;
/**
* Returns whether stream wrappers are supported
*
* @return bool
*/
protected function doesSupportStreamWrapper()
{
return false;
}
/** /**
* Opens the file at the given file path to make it ready to be read. * Opens the file at the given file path to make it ready to be read.
* *
+18 -5
View File
@@ -20,6 +20,7 @@ class RowIterator implements IteratorInterface
const XML_NODE_TABLE = 'table:table'; const XML_NODE_TABLE = 'table:table';
const XML_NODE_ROW = 'table:table-row'; const XML_NODE_ROW = 'table:table-row';
const XML_NODE_CELL = 'table:table-cell'; const XML_NODE_CELL = 'table:table-cell';
const MAX_COLUMNS_EXCEL = 16384;
/** Definition of XML attribute used to parse data */ /** Definition of XML attribute used to parse data */
const XML_ATTRIBUTE_NUM_COLUMNS_REPEATED = 'table:number-columns-repeated'; const XML_ATTRIBUTE_NUM_COLUMNS_REPEATED = 'table:number-columns-repeated';
@@ -127,22 +128,23 @@ class RowIterator implements IteratorInterface
} else if ($this->xmlReader->isPositionedOnEndingNode(self::XML_NODE_ROW)) { } else if ($this->xmlReader->isPositionedOnEndingNode(self::XML_NODE_ROW)) {
// End of the row description // End of the row description
$isEmptyRow = ($numCellsRead <= 1 && empty($cellValue)); $isEmptyRow = ($numCellsRead <= 1 && $this->isEmptyCellValue($cellValue));
if ($isEmptyRow) { if ($isEmptyRow) {
// skip empty rows // skip empty rows
$this->next(); $this->next();
return; return;
} }
// Only add value if the last read cell is not empty or does not need to repeat cell values. // Only add the value if the last read cell is not a trailing empty cell repeater in Excel.
// The current count of read columns is determined by counting the values in $rowData.
// This is to avoid creating a lot of empty cells, as Excel adds a last empty "<table:table-cell>" // This is to avoid creating a lot of empty cells, as Excel adds a last empty "<table:table-cell>"
// with a number-columns-repeated value equals to the number of (supported columns - used columns). // with a number-columns-repeated value equals to the number of (supported columns - used columns).
// In Excel, the number of supported columns is 16384, but we don't want to returns rows with always 16384 cells. // In Excel, the number of supported columns is 16384, but we don't want to returns rows with
if (!empty($cellValue) || $numColumnsRepeated === 1) { // always 16384 cells.
if ((count($rowData) + $numColumnsRepeated) !== self::MAX_COLUMNS_EXCEL) {
for ($i = 0; $i < $numColumnsRepeated; $i++) { for ($i = 0; $i < $numColumnsRepeated; $i++) {
$rowData[] = $cellValue; $rowData[] = $cellValue;
} }
$this->numReadRows++; $this->numReadRows++;
} }
break; break;
@@ -181,6 +183,17 @@ class RowIterator implements IteratorInterface
return $this->cellValueFormatter->extractAndFormatNodeValue($node); return $this->cellValueFormatter->extractAndFormatNodeValue($node);
} }
/**
* empty() replacement that honours 0 as a valid value
*
* @param $value The cell value
* @return bool
*/
protected function isEmptyCellValue($value)
{
return (!isset($value) || trim($value) === '');
}
/** /**
* Return the current element, from the buffer. * Return the current element, from the buffer.
* @link http://php.net/manual/en/iterator.current.php * @link http://php.net/manual/en/iterator.current.php
+10
View File
@@ -37,6 +37,16 @@ class Reader extends AbstractReader
return $this; return $this;
} }
/**
* Returns whether stream wrappers are supported
*
* @return bool
*/
protected function doesSupportStreamWrapper()
{
return false;
}
/** /**
* Opens the file at the given file path to make it ready to be read. * Opens the file at the given file path to make it ready to be read.
* It also parses the sharedStrings.xml file to get all the shared strings available in memory * It also parses the sharedStrings.xml file to get all the shared strings available in memory
+5
View File
@@ -319,6 +319,11 @@ abstract class AbstractWriter implements WriterInterface
public function close() public function close()
{ {
$this->closeWriter(); $this->closeWriter();
if (is_resource($this->filePointer)) {
$this->globalFunctionsHelper->fclose($this->filePointer);
}
$this->isWriterOpened = false; $this->isWriterOpened = false;
} }
} }
-4
View File
@@ -96,10 +96,6 @@ class Writer extends AbstractWriter
*/ */
protected function closeWriter() protected function closeWriter()
{ {
if ($this->filePointer) {
$this->globalFunctionsHelper->fclose($this->filePointer);
}
$this->lastWrittenRowIndex = 0; $this->lastWrittenRowIndex = 0;
} }
} }
+1 -1
View File
@@ -146,7 +146,7 @@ EOD;
$cellXML .= ' t="s"><v>' . $sharedStringId . '</v></c>'; $cellXML .= ' t="s"><v>' . $sharedStringId . '</v></c>';
} }
} else if (CellHelper::isBoolean($cellValue)) { } else if (CellHelper::isBoolean($cellValue)) {
$cellXML .= ' t="b"><v>' . $cellValue . '</v></c>'; $cellXML .= ' t="b"><v>' . intval($cellValue) . '</v></c>';
} else if (CellHelper::isNumeric($cellValue)) { } else if (CellHelper::isNumeric($cellValue)) {
$cellXML .= '><v>' . $cellValue . '</v></c>'; $cellXML .= '><v>' . $cellValue . '</v></c>';
} else if (empty($cellValue)) { } else if (empty($cellValue)) {
+87
View File
@@ -204,6 +204,15 @@ class ReaderTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('This is, a comma', $allRows[0][0]); $this->assertEquals('This is, a comma', $allRows[0][0]);
} }
/**
* @return void
*/
public function testReadShouldNotTruncateLineBreak()
{
$allRows = $this->getAllRowsForFile('csv_with_line_breaks.csv', ',');
$this->assertEquals("This is,\na comma", $allRows[0][0]);
}
/** /**
* @return array * @return array
*/ */
@@ -343,6 +352,38 @@ class ReaderTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($expectedRows, $allRows); $this->assertEquals($expectedRows, $allRows);
} }
/**
* https://github.com/box/spout/issues/184
* @return void
*/
public function testReadShouldInludeRowsWithZerosOnly()
{
$allRows = $this->getAllRowsForFile('sheet_with_zeros_in_row.csv');
$expectedRows = [
['A', 'B', 'C'],
['1', '2', '3'],
['0', '0', '0']
];
$this->assertEquals($expectedRows, $allRows, 'There should be only 3 rows, because zeros (0) are valid values');
}
/**
* https://github.com/box/spout/issues/184
* @return void
*/
public function testReadShouldCreateOutputEmptyCellPreserved()
{
$allRows = $this->getAllRowsForFile('sheet_with_empty_cells.csv');
$expectedRows = [
['A', 'B', 'C'],
['0', '', ''],
['1', '1', '']
];
$this->assertEquals($expectedRows, $allRows, 'There should be 3 rows, with equal length');
}
/** /**
* @param string $fileName * @param string $fileName
* @param string|void $fieldDelimiter * @param string|void $fieldDelimiter
@@ -423,4 +464,50 @@ class ReaderTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($expectedRows, $allRows); $this->assertEquals($expectedRows, $allRows);
} }
/**
* @return void
*/
public function testReadCustomStreamWrapper()
{
$allRows = [];
$resourcePath = 'spout://csv_standard';
// register stream wrapper
stream_wrapper_register('spout', SpoutTestStream::CLASS_NAME);
/** @var \Box\Spout\Reader\CSV\Reader $reader */
$reader = ReaderFactory::create(Type::CSV);
$reader->open($resourcePath);
foreach ($reader->getSheetIterator() as $sheet) {
foreach ($sheet->getRowIterator() as $row) {
$allRows[] = $row;
}
}
$reader->close();
$expectedRows = [
['csv--11', 'csv--12', 'csv--13'],
['csv--21', 'csv--22', 'csv--23'],
['csv--31', 'csv--32', 'csv--33'],
];
$this->assertEquals($expectedRows, $allRows);
// cleanup
stream_wrapper_unregister('spout');
}
/**
* @expectedException \Box\Spout\Common\Exception\IOException
*
* @return void
*/
public function testReadWithUnsupportedCustomStreamWrapper()
{
/** @var \Box\Spout\Reader\CSV\Reader $reader */
$reader = ReaderFactory::create(Type::CSV);
$reader->open('unsupported://foobar');
}
} }
+120
View File
@@ -0,0 +1,120 @@
<?php
namespace Box\Spout\Reader\CSV;
/**
* Class SpoutTestStream
* Custom stream that reads CSV files located in the tests/resources/csv folder.
* For example: spout://foobar will point to tests/resources/csv/foobar.csv
*
* @package Box\Spout\Reader\CSV
*/
class SpoutTestStream
{
const CLASS_NAME = __CLASS__;
const PATH_TO_CSV_RESOURCES = 'tests/resources/csv/';
const CSV_EXTENSION = '.csv';
/** @var int $position */
private $position;
/** @var resource $fileHandle */
private $fileHandle;
/**
* @param string $path
* @param int $flag
* @return array
*/
public function url_stat($path, $flag)
{
$filePath = $this->getFilePathFromStreamPath($path);
return stat($filePath);
}
/**
* @param string $streamPath
* @return string
*/
private function getFilePathFromStreamPath($streamPath)
{
$fileName = parse_url($streamPath, PHP_URL_HOST);
return self::PATH_TO_CSV_RESOURCES . $fileName . self::CSV_EXTENSION;
}
/**
* @param string $path
* @param string $mode
* @param int $options
* @param string $opened_path
* @return bool
*/
public function stream_open($path, $mode, $options, &$opened_path)
{
$this->position = 0;
// the path is like "spout://csv_name" so the actual file name correspond the name of the host.
$filePath = $this->getFilePathFromStreamPath($path);
$this->fileHandle = fopen($filePath, $mode);
return true;
}
/**
* @param int $numBytes
* @return string
*/
public function stream_read($numBytes)
{
$this->position += $numBytes;
return fread($this->fileHandle, $numBytes);
}
/**
* @return int
*/
public function stream_tell()
{
return $this->position;
}
/**
* @param int $offset
* @param int|void $whence
* @return bool
*/
public function stream_seek($offset, $whence = SEEK_SET)
{
$result = fseek($this->fileHandle, $offset, $whence);
if ($result === -1) {
return false;
}
if ($whence === SEEK_SET) {
$this->position = $offset;
} else if ($whence === SEEK_CUR) {
$this->position += $offset;
} else {
// not implemented
}
return true;
}
/**
* @return bool
*/
public function stream_close()
{
return fclose($this->fileHandle);
}
/**
* @return bool
*/
public function stream_eof()
{
return feof($this->fileHandle);
}
}
+56
View File
@@ -363,6 +363,62 @@ class ReaderTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($expectedRows, $allRows); $this->assertEquals($expectedRows, $allRows);
} }
/**
* @expectedException \Box\Spout\Common\Exception\IOException
*
* @return void
*/
public function testReadWithUnsupportedCustomStreamWrapper()
{
/** @var \Box\Spout\Reader\ODS\Reader $reader */
$reader = ReaderFactory::create(Type::ODS);
$reader->open('unsupported://foobar');
}
/**
* @expectedException \Box\Spout\Common\Exception\IOException
*
* @return void
*/
public function testReadWithSupportedCustomStreamWrapper()
{
/** @var \Box\Spout\Reader\ODS\Reader $reader */
$reader = ReaderFactory::create(Type::ODS);
$reader->open('php://memory');
}
/**
* https://github.com/box/spout/issues/184
* @return void
*/
public function testReadShouldInludeRowsWithZerosOnly()
{
$allRows = $this->getAllRowsForFile('sheet_with_zeros_in_row.ods');
$expectedRows = [
['A', 'B', 'C'],
['1', '2', '3'],
['0', '0', '0']
];
$this->assertEquals($expectedRows, $allRows, 'There should be only 3 rows, because zeros (0) are valid values');
}
/**
* https://github.com/box/spout/issues/184
* @return void
*/
public function testReadShouldCreateOutputEmptyCellPreserved()
{
$allRows = $this->getAllRowsForFile('sheet_with_empty_cells.ods');
$expectedRows = [
['A', 'B', 'C'],
['0', '', ''],
['1', '1', '']
];
$this->assertEquals($expectedRows, $allRows, 'There should be 3 rows, with equal length');
}
/** /**
* @param string $fileName * @param string $fileName
* @return array All the read rows the given file * @return array All the read rows the given file
+57
View File
@@ -404,6 +404,63 @@ class ReaderTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($expectedRows, $allRows); $this->assertEquals($expectedRows, $allRows);
} }
/**
* @expectedException \Box\Spout\Common\Exception\IOException
*
* @return void
*/
public function testReadWithUnsupportedCustomStreamWrapper()
{
/** @var \Box\Spout\Reader\XLSX\Reader $reader */
$reader = ReaderFactory::create(Type::XLSX);
$reader->open('unsupported://foobar');
}
/**
* @expectedException \Box\Spout\Common\Exception\IOException
*
* @return void
*/
public function testReadWithSupportedCustomStreamWrapper()
{
/** @var \Box\Spout\Reader\XLSX\Reader $reader */
$reader = ReaderFactory::create(Type::XLSX);
$reader->open('php://memory');
}
/**
* https://github.com/box/spout/issues/184
* @return void
*/
public function testReadShouldInludeRowsWithZerosOnly()
{
$allRows = $this->getAllRowsForFile('sheet_with_zeros_in_row.xlsx');
$expectedRows = [
['A', 'B', 'C'],
['1', '2', '3'],
['0', '0', '0']
];
$this->assertEquals($expectedRows, $allRows, 'There should be only 3 rows, because zeros (0) are valid values');
}
/**
* https://github.com/box/spout/issues/184
* @return void
*/
public function testReadShouldCreateOutputEmptyCellPreserved()
{
$allRows = $this->getAllRowsForFile('sheet_with_empty_cells.xlsx');
$expectedRows = [
['A', 'B', 'C'],
['0', '', ''],
['1', '1', '']
];
$this->assertEquals($expectedRows, $allRows, 'There should be 3 rows, with equal length');
}
/** /**
* @param string $fileName * @param string $fileName
* @return array All the read rows the given file * @return array All the read rows the given file
+1
View File
@@ -3,6 +3,7 @@ require_once(dirname(__DIR__) . '/vendor/autoload.php');
require_once(dirname(__DIR__) . '/tests/Spout/TestUsingResource.php'); require_once(dirname(__DIR__) . '/tests/Spout/TestUsingResource.php');
require_once(dirname(__DIR__) . '/tests/Spout/ReflectionHelper.php'); require_once(dirname(__DIR__) . '/tests/Spout/ReflectionHelper.php');
require_once(dirname(__DIR__) . '/tests/Spout/Reader/CSV/SpoutTestStream.php');
// Make sure a timezone is set to be able to work with dates // Make sure a timezone is set to be able to work with dates
date_default_timezone_set('UTC'); date_default_timezone_set('UTC');
@@ -0,0 +1,2 @@
"This is,
a comma",csv--12
1 This is, a comma csv--12
@@ -0,0 +1,3 @@
"A","B","C"
0,,
1,1,
1 A B C
2 0
3 1 1
@@ -0,0 +1,3 @@
"A","B","C"
1,2,3
0,0,0
1 A B C
2 1 2 3
3 0 0 0
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.