diff --git a/src/batch-box-spout/src/FlatFileReader.php b/src/batch-box-spout/src/FlatFileReader.php index 87a0c59b..eca646bb 100644 --- a/src/batch-box-spout/src/FlatFileReader.php +++ b/src/batch-box-spout/src/FlatFileReader.php @@ -14,6 +14,7 @@ use Yokai\Batch\Job\Item\ItemReaderInterface; use Yokai\Batch\Job\JobExecutionAwareInterface; use Yokai\Batch\Job\JobExecutionAwareTrait; +use Yokai\Batch\Warning; final class FlatFileReader implements ItemReaderInterface, @@ -110,7 +111,35 @@ public function read(): iterable } if (is_array($headers)) { - $row = array_combine($headers, $row); + try { + /** @var array|false $combined */ + $combined = @array_combine($headers, $row); + if ($combined === false) { + // @codeCoverageIgnoreStart + // Prior to PHP 8.0 array_combine only trigger a warning + // Now it is throwing a ValueError + throw new \ValueError( + 'array_combine(): Argument #1 ($keys) and argument #2 ($values) ' . + 'must have the same number of elements' + ); + // @codeCoverageIgnoreEnd + } + } catch (\ValueError $exception) { + $this->jobExecution->addWarning( + new Warning( + 'Expecting row {row} to have exactly {expected} columns(s), but got {actual}.', + [ + '{row}' => (string)$rowIndex, + '{expected}' => (string)count($headers), + '{actual}' => (string)count($row), + ], + ['headers' => $headers, 'row' => $row] + ) + ); + continue; + } + + $row = $combined; } yield $row; diff --git a/src/batch-box-spout/tests/FlatFileReaderTest.php b/src/batch-box-spout/tests/FlatFileReaderTest.php index b2307a97..64f2c538 100644 --- a/src/batch-box-spout/tests/FlatFileReaderTest.php +++ b/src/batch-box-spout/tests/FlatFileReaderTest.php @@ -57,6 +57,48 @@ public function testMissingFileToRead(string $type) iterator_to_array($reader->read()); } + public function testReadWrongLineSize(): void + { + $file = __DIR__ . '/fixtures/wrong-line-size.csv'; + $jobExecution = JobExecution::createRoot('123456789', 'parent'); + $reader = new FlatFileReader( + 'csv', + [], + FlatFileReader::HEADERS_MODE_COMBINE, + null, + $file + ); + $reader->setJobExecution($jobExecution); + + /** @var \Iterator $result */ + $result = $reader->read(); + self::assertInstanceOf(\Iterator::class, $result); + self::assertSame( + [ + ['firstName' => 'John', 'lastName' => 'Doe'], + ['firstName' => 'Jack', 'lastName' => 'Doe'], + ], + iterator_to_array($result) + ); + + self::assertSame( + 'Expecting row {row} to have exactly {expected} columns(s), but got {actual}.', + $jobExecution->getWarnings()[0]->getMessage() + ); + self::assertSame( + [ + '{row}' => '3', + '{expected}' => '2', + '{actual}' => '3', + ], + $jobExecution->getWarnings()[0]->getParameters() + ); + self::assertSame( + ['headers' => ['firstName', 'lastName'], 'row' => ['Jane', 'Doe', 'too much data']], + $jobExecution->getWarnings()[0]->getContext() + ); + } + public function types() { foreach ([Type::CSV, Type::XLSX, Type::ODS] as $type) { diff --git a/src/batch-box-spout/tests/fixtures/wrong-line-size.csv b/src/batch-box-spout/tests/fixtures/wrong-line-size.csv new file mode 100644 index 00000000..c5985ddc --- /dev/null +++ b/src/batch-box-spout/tests/fixtures/wrong-line-size.csv @@ -0,0 +1,4 @@ +firstName,lastName +John,Doe +Jane,Doe,too much data +Jack,Doe