Skip to content

Commit

Permalink
Merge aaf9414 into 6a3d0e6
Browse files Browse the repository at this point in the history
  • Loading branch information
nyamsprod committed Aug 28, 2019
2 parents 6a3d0e6 + aaf9414 commit a399d6d
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 6 deletions.
2 changes: 1 addition & 1 deletion src/Polyfill/EmptyEscapeParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ public static function parse($document): Generator
self::$document->rewind();
while (self::$document->valid()) {
$record = self::extractRecord();
if (!in_array(null, $record, true)) {
if ([null] === $record || !in_array(null, $record, true)) {
yield $record;
}
}
Expand Down
60 changes: 56 additions & 4 deletions src/Reader.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
use function array_pad;
use function array_slice;
use function array_unique;
use function count;
use function gettype;
use function is_array;
use function iterator_count;
Expand Down Expand Up @@ -75,6 +76,11 @@ class Reader extends AbstractCsv implements Countable, IteratorAggregate, JsonSe
*/
protected $stream_filter_mode = STREAM_FILTER_READ;

/**
* @var bool
*/
protected $is_empty_records_skipped = true;

/**
* {@inheritdoc}
*/
Expand Down Expand Up @@ -137,7 +143,7 @@ public function getHeader(): array
protected function setHeader(int $offset): array
{
$header = $this->seekRow($offset);
if (false === $header || [] === $header) {
if (false === $header || [] === $header || [null] === $header) {
throw new Exception(sprintf('The header record does not exist or is empty at offset: `%s`', $offset));
}

Expand Down Expand Up @@ -175,7 +181,7 @@ protected function getDocument(): Iterator
return EmptyEscapeParser::parse($this->document);
}

$this->document->setFlags(SplFileObject::READ_CSV | SplFileObject::READ_AHEAD | SplFileObject::SKIP_EMPTY);
$this->document->setFlags(SplFileObject::READ_CSV | SplFileObject::READ_AHEAD);
$this->document->setCsvControl($this->delimiter, $this->enclosure, $this->escape);
$this->document->rewind();

Expand Down Expand Up @@ -263,8 +269,8 @@ public function jsonSerialize(): array
public function getRecords(array $header = []): Iterator
{
$header = $this->computeHeader($header);
$normalized = static function ($record): bool {
return is_array($record) && $record != [null];
$normalized = function ($record): bool {
return is_array($record) && (!$this->is_empty_records_skipped || $record != [null]);
};
$bom = $this->getInputBOM();
$document = $this->getDocument();
Expand All @@ -276,6 +282,18 @@ public function getRecords(array $header = []): Iterator
});
}

if (!$this->is_empty_records_skipped) {
$normalized_empty_records = static function (array $record): array {
if ([null] === $record) {
return [];
}

return $record;
};

return $this->combineHeader(new MapIterator($records, $normalized_empty_records), $header);
}

return $this->combineHeader($records, $header);
}

Expand Down Expand Up @@ -376,4 +394,38 @@ public function setHeaderOffset($offset): self

return $this;
}

/**
* Enable skipping empty records.
*/
public function enableEmptyRecordsSkipping(): self
{
if (!$this->is_empty_records_skipped) {
$this->is_empty_records_skipped = true;
$this->nb_records = -1;
}

return $this;
}

/**
* Disable skipping empty records.
*/
public function disableEmptyRecordsSkipping(): self
{
if ($this->is_empty_records_skipped) {
$this->is_empty_records_skipped = false;
$this->nb_records = -1;
}

return $this;
}

/**
* Tells whether empty records are skipped by the instance.
*/
public function isEmptyRecordsSkipped(): bool
{
return $this->is_empty_records_skipped;
}
}
4 changes: 3 additions & 1 deletion tests/Polyfill/EmptyEscapeParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ public function testWorksWithMultiLinesWithDifferentDelimiter()
* @covers ::extractFieldContent
* @covers ::extractEnclosedFieldContent
*/
public function testRemoveEmptyLines()
public function testPreserveEmptyLines()
{
$source = <<<EOF
"parent name","child name","title"
Expand All @@ -114,6 +114,8 @@ public function testRemoveEmptyLines()

$expected = [
['parent name', 'child name', 'title'],
[null],
[null],
['parentA', 'childA', 'titleA'],
];

Expand Down
79 changes: 79 additions & 0 deletions tests/ReaderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -457,4 +457,83 @@ public function testCreateFromPath()
$csv = Reader::createFromPath(__DIR__.'/data/foo_readonly.csv');
self::assertCount(1, $csv);
}

/**
* @dataProvider sourceProvider
* @covers ::disableEmptyRecordsSkipping
* @covers ::enableEmptyRecordsSkipping
* @covers ::isEmptyRecordsSkipped
* @covers ::getRecords
*/
public function testSkippingEmptyRecords(Reader $reader, array $expected_with_skipping, array $expected_with_no_skipping)
{
self::assertCount(2, $reader);
self::assertTrue($reader->isEmptyRecordsSkipped());
foreach ($reader as $offset => $record) {
self::assertSame($expected_with_skipping[$offset], $record);
}

$reader->disableEmptyRecordsSkipping();
self::assertFalse($reader->isEmptyRecordsSkipped());
self::assertCount(4, $reader);
foreach ($reader as $offset => $record) {
self::assertSame($expected_with_no_skipping[$offset], $record);
}

$reader->enableEmptyRecordsSkipping();
self::assertCount(2, $reader);
self::assertTrue($reader->isEmptyRecordsSkipped());
foreach ($reader as $offset => $record) {
self::assertSame($expected_with_skipping[$offset], $record);
}
}

public function sourceProvider(): array
{
$source = <<<EOF
"parent name","child name","title"
"parentA","childA","titleA"
EOF;
$expected_with_no_skipping = [
['parent name', 'child name', 'title'],
[],
[],
['parentA', 'childA', 'titleA'],
];

$expected_with_skipping = [
['parent name', 'child name', 'title'],
[],
[],
['parentA', 'childA', 'titleA'],
];

$rsrc = new SplTempFileObject();
$rsrc->fwrite($source);

return [
'FileObject' => [
Reader::createFromFileObject($rsrc),
$expected_with_skipping,
$expected_with_no_skipping,
],
'Stream' => [
Reader::createFromString($source),
$expected_with_skipping,
$expected_with_no_skipping,
],
'FileObject with empty escape char' => [
Reader::createFromFileObject($rsrc)->setEscape(''),
$expected_with_skipping,
$expected_with_no_skipping,
],
'Stream with empty escape char' => [
Reader::createFromString($source)->setEscape(''),
$expected_with_skipping,
$expected_with_no_skipping,
],
];
}
}

0 comments on commit a399d6d

Please sign in to comment.