Skip to content

Commit f546a33

Browse files
authoredAug 23, 2021
Add reader for txt files with fixed columns size (#22)
* Add reader for txt files with fixed columns size * Updated fixed size file reader with latest changed from 0.x * Fixed static analyis and checkstyle * Fixed static analysis
1 parent d9df3a9 commit f546a33

File tree

3 files changed

+203
-0
lines changed

3 files changed

+203
-0
lines changed
 
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Yokai\Batch\Job\Item\Reader\Filesystem;
6+
7+
use Generator;
8+
use Yokai\Batch\Exception\RuntimeException;
9+
use Yokai\Batch\Exception\UnexpectedValueException;
10+
use Yokai\Batch\Job\Item\ItemReaderInterface;
11+
use Yokai\Batch\Job\JobExecutionAwareInterface;
12+
use Yokai\Batch\Job\JobExecutionAwareTrait;
13+
use Yokai\Batch\Job\Parameters\JobParameterAccessorInterface;
14+
15+
final class FixedColumnSizeFileReader implements
16+
ItemReaderInterface,
17+
JobExecutionAwareInterface
18+
{
19+
use JobExecutionAwareTrait;
20+
21+
public const HEADERS_MODE_SKIP = 'skip';
22+
public const HEADERS_MODE_COMBINE = 'combine';
23+
public const HEADERS_MODE_NONE = 'none';
24+
private const AVAILABLE_HEADERS_MODES = [
25+
self::HEADERS_MODE_SKIP,
26+
self::HEADERS_MODE_COMBINE,
27+
self::HEADERS_MODE_NONE,
28+
];
29+
30+
/**
31+
* @var int[]
32+
*/
33+
private array $columns;
34+
35+
/**
36+
* @var string
37+
*/
38+
private string $headersMode;
39+
40+
/**
41+
* @var JobParameterAccessorInterface
42+
*/
43+
private JobParameterAccessorInterface $filePath;
44+
45+
/**
46+
* @param int[] $columns
47+
*/
48+
public function __construct(
49+
array $columns,
50+
JobParameterAccessorInterface $filePath,
51+
string $headersMode = self::HEADERS_MODE_NONE
52+
) {
53+
if (!\in_array($headersMode, self::AVAILABLE_HEADERS_MODES, true)) {
54+
throw UnexpectedValueException::enum(self::AVAILABLE_HEADERS_MODES, $headersMode, 'Invalid header mode.');
55+
}
56+
57+
$this->columns = $columns;
58+
$this->headersMode = $headersMode;
59+
$this->filePath = $filePath;
60+
}
61+
62+
/**
63+
* @inheritdoc
64+
* @phpstan-return Generator<array<mixed>>
65+
*/
66+
public function read(): Generator
67+
{
68+
$path = (string)$this->filePath->get($this->jobExecution);
69+
$handle = @\fopen($path, 'r');
70+
if ($handle === false) {
71+
throw new RuntimeException(\sprintf('Cannot read %s.', $path));
72+
}
73+
74+
$headers = \array_keys($this->columns);
75+
76+
$index = -1;
77+
78+
while (($line = \fgets($handle)) !== false) {
79+
$index++;
80+
81+
$start = 0;
82+
$row = [];
83+
foreach ($this->columns as $size) {
84+
$row[] = \trim(\mb_substr($line, $start, $size));
85+
$start += $size;
86+
}
87+
88+
if ($index === 0) {
89+
if ($this->headersMode === self::HEADERS_MODE_COMBINE) {
90+
$headers = $row;
91+
}
92+
if (\in_array($this->headersMode, [self::HEADERS_MODE_COMBINE, self::HEADERS_MODE_SKIP], true)) {
93+
continue;
94+
}
95+
}
96+
97+
if (\is_array($headers)) {
98+
$row = \array_combine($headers, $row);
99+
}
100+
101+
yield $row;
102+
}
103+
104+
\fclose($handle);
105+
}
106+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Yokai\Batch\Tests\Job\Item\Reader\Filesystem;
6+
7+
use Generator;
8+
use PHPUnit\Framework\TestCase;
9+
use Yokai\Batch\Exception\RuntimeException;
10+
use Yokai\Batch\Exception\UnexpectedValueException;
11+
use Yokai\Batch\Job\Item\Reader\Filesystem\FixedColumnSizeFileReader;
12+
use Yokai\Batch\Job\Parameters\StaticValueParameterAccessor;
13+
use Yokai\Batch\JobExecution;
14+
15+
class FixedColumnSizeFileReaderTest extends TestCase
16+
{
17+
/**
18+
* @dataProvider config
19+
*/
20+
public function test(array $columns, string $headersMode, array $expected): void
21+
{
22+
$execution = JobExecution::createRoot('123456', 'testing');
23+
$reader = new FixedColumnSizeFileReader(
24+
$columns,
25+
new StaticValueParameterAccessor(__DIR__ . '/fixtures/fixed-column-size.txt'),
26+
$headersMode
27+
);
28+
$reader->setJobExecution($execution);
29+
self::assertSame($expected, \iterator_to_array($reader->read()));
30+
}
31+
32+
public function testInvalidHeaderMode(): void
33+
{
34+
$this->expectException(UnexpectedValueException::class);
35+
new FixedColumnSizeFileReader([10, 10], new StaticValueParameterAccessor(null), 'wrong header mode');
36+
}
37+
38+
public function testFileNotFound(): void
39+
{
40+
$this->expectException(RuntimeException::class);
41+
$execution = JobExecution::createRoot('123456', 'testing');
42+
$reader = new FixedColumnSizeFileReader(
43+
[10, 10],
44+
new StaticValueParameterAccessor(__DIR__ . '/fixtures/unknown-file.ext')
45+
);
46+
$reader->setJobExecution($execution);
47+
\iterator_to_array($reader->read());
48+
}
49+
50+
public function config(): Generator
51+
{
52+
$columnsWithoutNames = [10, 9, 8, -1];
53+
$columnsWithNames = ['firstName' => 10, 'lastName' => 9, 'country' => 8, 'city' => -1];
54+
55+
yield [
56+
$columnsWithoutNames,
57+
FixedColumnSizeFileReader::HEADERS_MODE_COMBINE,
58+
[
59+
['firstName' => 'John', 'lastName' => 'Doe', 'country' => 'USA', 'city' => 'Washington'],
60+
['firstName' => 'Jane', 'lastName' => 'Doe', 'country' => 'USA', 'city' => 'Seattle'],
61+
['firstName' => 'Jack', 'lastName' => 'Doe', 'country' => 'USA', 'city' => 'San Francisco'],
62+
],
63+
];
64+
yield [
65+
$columnsWithNames,
66+
FixedColumnSizeFileReader::HEADERS_MODE_SKIP,
67+
[
68+
['firstName' => 'John', 'lastName' => 'Doe', 'country' => 'USA', 'city' => 'Washington'],
69+
['firstName' => 'Jane', 'lastName' => 'Doe', 'country' => 'USA', 'city' => 'Seattle'],
70+
['firstName' => 'Jack', 'lastName' => 'Doe', 'country' => 'USA', 'city' => 'San Francisco'],
71+
],
72+
];
73+
yield [
74+
$columnsWithoutNames,
75+
FixedColumnSizeFileReader::HEADERS_MODE_NONE,
76+
[
77+
['firstName', 'lastName', 'country', 'city'],
78+
['John', 'Doe', 'USA', 'Washington'],
79+
['Jane', 'Doe', 'USA', 'Seattle'],
80+
['Jack', 'Doe', 'USA', 'San Francisco'],
81+
],
82+
];
83+
yield [
84+
$columnsWithoutNames,
85+
FixedColumnSizeFileReader::HEADERS_MODE_SKIP,
86+
[
87+
['John', 'Doe', 'USA', 'Washington'],
88+
['Jane', 'Doe', 'USA', 'Seattle'],
89+
['Jack', 'Doe', 'USA', 'San Francisco'],
90+
],
91+
];
92+
}
93+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
firstName lastName country city
2+
John Doe USA Washington
3+
Jane Doe USA Seattle
4+
Jack Doe USA San Francisco

0 commit comments

Comments
 (0)