diff --git a/src/Job/Item/Reader/Filesystem/FixedColumnSizeFileReader.php b/src/Job/Item/Reader/Filesystem/FixedColumnSizeFileReader.php new file mode 100644 index 0000000..213b18c --- /dev/null +++ b/src/Job/Item/Reader/Filesystem/FixedColumnSizeFileReader.php @@ -0,0 +1,106 @@ +columns = $columns; + $this->headersMode = $headersMode; + $this->filePath = $filePath; + } + + /** + * @inheritdoc + * @phpstan-return Generator> + */ + public function read(): Generator + { + $path = (string)$this->filePath->get($this->jobExecution); + $handle = @\fopen($path, 'r'); + if ($handle === false) { + throw new RuntimeException(\sprintf('Cannot read %s.', $path)); + } + + $headers = \array_keys($this->columns); + + $index = -1; + + while (($line = \fgets($handle)) !== false) { + $index++; + + $start = 0; + $row = []; + foreach ($this->columns as $size) { + $row[] = \trim(\mb_substr($line, $start, $size)); + $start += $size; + } + + if ($index === 0) { + if ($this->headersMode === self::HEADERS_MODE_COMBINE) { + $headers = $row; + } + if (\in_array($this->headersMode, [self::HEADERS_MODE_COMBINE, self::HEADERS_MODE_SKIP], true)) { + continue; + } + } + + if (\is_array($headers)) { + $row = \array_combine($headers, $row); + } + + yield $row; + } + + \fclose($handle); + } +} diff --git a/tests/Job/Item/Reader/Filesystem/FixedColumnSizeFileReaderTest.php b/tests/Job/Item/Reader/Filesystem/FixedColumnSizeFileReaderTest.php new file mode 100644 index 0000000..e52c554 --- /dev/null +++ b/tests/Job/Item/Reader/Filesystem/FixedColumnSizeFileReaderTest.php @@ -0,0 +1,93 @@ +setJobExecution($execution); + self::assertSame($expected, \iterator_to_array($reader->read())); + } + + public function testInvalidHeaderMode(): void + { + $this->expectException(UnexpectedValueException::class); + new FixedColumnSizeFileReader([10, 10], new StaticValueParameterAccessor(null), 'wrong header mode'); + } + + public function testFileNotFound(): void + { + $this->expectException(RuntimeException::class); + $execution = JobExecution::createRoot('123456', 'testing'); + $reader = new FixedColumnSizeFileReader( + [10, 10], + new StaticValueParameterAccessor(__DIR__ . '/fixtures/unknown-file.ext') + ); + $reader->setJobExecution($execution); + \iterator_to_array($reader->read()); + } + + public function config(): Generator + { + $columnsWithoutNames = [10, 9, 8, -1]; + $columnsWithNames = ['firstName' => 10, 'lastName' => 9, 'country' => 8, 'city' => -1]; + + yield [ + $columnsWithoutNames, + FixedColumnSizeFileReader::HEADERS_MODE_COMBINE, + [ + ['firstName' => 'John', 'lastName' => 'Doe', 'country' => 'USA', 'city' => 'Washington'], + ['firstName' => 'Jane', 'lastName' => 'Doe', 'country' => 'USA', 'city' => 'Seattle'], + ['firstName' => 'Jack', 'lastName' => 'Doe', 'country' => 'USA', 'city' => 'San Francisco'], + ], + ]; + yield [ + $columnsWithNames, + FixedColumnSizeFileReader::HEADERS_MODE_SKIP, + [ + ['firstName' => 'John', 'lastName' => 'Doe', 'country' => 'USA', 'city' => 'Washington'], + ['firstName' => 'Jane', 'lastName' => 'Doe', 'country' => 'USA', 'city' => 'Seattle'], + ['firstName' => 'Jack', 'lastName' => 'Doe', 'country' => 'USA', 'city' => 'San Francisco'], + ], + ]; + yield [ + $columnsWithoutNames, + FixedColumnSizeFileReader::HEADERS_MODE_NONE, + [ + ['firstName', 'lastName', 'country', 'city'], + ['John', 'Doe', 'USA', 'Washington'], + ['Jane', 'Doe', 'USA', 'Seattle'], + ['Jack', 'Doe', 'USA', 'San Francisco'], + ], + ]; + yield [ + $columnsWithoutNames, + FixedColumnSizeFileReader::HEADERS_MODE_SKIP, + [ + ['John', 'Doe', 'USA', 'Washington'], + ['Jane', 'Doe', 'USA', 'Seattle'], + ['Jack', 'Doe', 'USA', 'San Francisco'], + ], + ]; + } +} diff --git a/tests/Job/Item/Reader/Filesystem/fixtures/fixed-column-size.txt b/tests/Job/Item/Reader/Filesystem/fixtures/fixed-column-size.txt new file mode 100644 index 0000000..53836bd --- /dev/null +++ b/tests/Job/Item/Reader/Filesystem/fixtures/fixed-column-size.txt @@ -0,0 +1,4 @@ +firstName lastName country city +John Doe USA Washington +Jane Doe USA Seattle +Jack Doe USA San Francisco