Passed
Push — master ( 0e87cd...c4ac3e )
by Carlos C
12:45 queued 01:03
created

CsvFolderJoinFiles::obtainFileLastLine()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 2
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 4
ccs 2
cts 2
cp 1
crap 2
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace PhpCfdi\SatCatalogosPopulate\Converters;
6
7
use Iterator;
8
use SplFileObject;
9
10
use function PhpCfdi\SatCatalogosPopulate\Utils\array_rtrim;
11
12
class CsvFolderJoinFiles
13
{
14 4
    public function joinFilesInFolder(string $csvFolder): void
15
    {
16 4
        $destinations = $this->obtainFilesThatAreSplitted($csvFolder);
17
18 4
        foreach ($destinations as $destination => $files) {
19 4
            $this->joinFilesToDestination(array_values($files), $destination);
20
        }
21
    }
22
23
    /**
24
     * @param array<int, string> $files
25
     */
26 5
    public function joinFilesToDestination(array $files, string $destination): void
27
    {
28 5
        $skipFirstLines = 0;
29 5
        $firstSource = $files[0];
30 5
        file_put_contents($destination, ''); // clear the file contents
31 5
        foreach ($files as $i => $source) {
32 5
            if ($i > 0 && 0 === $skipFirstLines) {
33 5
                $skipFirstLines = $this->findLinesToSkip($firstSource, $source);
34
            }
35 5
            $skipLastLines = 0;
36 5
            if ($this->lastLineContains($source, ['Continúa en'])) {
37 5
                $skipLastLines = 1;
38
            }
39 5
            $this->writeLines($source, $destination, $skipFirstLines, $skipLastLines);
40
        }
41
    }
42
43
    /**
44
     * @return array<string, array<int, string>>
45
     */
46 6
    public function obtainFilesThatAreSplitted(string $csvFolder): array
47
    {
48 6
        $files = array_filter(
49 6
            array_map(
50 6
                function ($path): array {
51 6
                    $file = basename($path);
52 6
                    $matches = [];
53
                    if (
54 6
                        ! preg_match('/^ *(.+)_Parte_([0-9]+) *\.csv$/', $file, $matches)
55 6
                        && ! preg_match('/^ *(.+) \(Parte ([0-9]+)\) *\.csv$/', $file, $matches)
56 6
                        && ! preg_match('/^ *(.+)_([0-9]+) *\.csv$/', $file, $matches)
57
                    ) {
58 4
                        return [];
59
                    }
60
                    return [
61 6
                        'destination' => dirname($path) . '/' . trim($matches[1]) . '.csv',
62 6
                        'index' => (int) $matches[2],
63
                        'source' => $path,
64
                    ];
65
                },
66 6
                glob($csvFolder . '/*.csv') ?: []
67
            )
68
        );
69
70 6
        uasort($files, $this->compareFiles(...));
0 ignored issues
show
Bug introduced by
A parse error occurred: Syntax error, unexpected ')' on line 70 at column 46
Loading history...
71
72 6
        $destinations = [];
73 6
        foreach ($files as $file) {
74 6
            $destinations[strval($file['destination'])][] = strval($file['source']);
75
        }
76
77 6
        return $destinations;
78
    }
79
80
    /**
81
     * @param array{destination: string, index: int} $first
82
     * @param array{destination: string, index: int} $second
83
     */
84 6
    private function compareFiles(array $first, array $second): int
85
    {
86 6
        return ($first['destination'] <=> $second['destination']) ?: $first['index'] <=> $second['index'];
87
    }
88
89 5
    public function writeLines(string $source, string $destination, int $skipFirstLines, int $skipLastLines): void
90
    {
91 5
        $command = implode(' ', [
92 5
            'cat ' . escapeshellarg($source),                   // send the file to the pipes
93 5
            sprintf('| tail -n +%d', $skipFirstLines + 1),      // without firsts n lines
94 5
            sprintf('| head -n -%d', $skipLastLines),           // without last n lines
95 5
            '>> ' . escapeshellarg($destination),               // create/append to destination
96
        ]);
97 5
        shell_exec($command);
98
    }
99
100 6
    public function findLinesToSkip(string $firstPath, string $secondPath): int
101
    {
102 6
        $first = new SplFileObject($firstPath, 'r');
103 6
        $second = new SplFileObject($secondPath, 'r');
104
105 6
        return $this->findLinesToSkipFromIterators($first, $second);
106
    }
107
108
    /**
109
     * @param Iterator<string> $first
110
     * @param Iterator<string> $second
111
     */
112 10
    public function findLinesToSkipFromIterators(Iterator $first, Iterator $second): int
113
    {
114 10
        $firstTenLines = $this->splReadTenLines($first);
115 10
        $secondTenLines = $this->splReadTenLines($second);
116
117 10
        for ($i = 9; $i > 0; $i--) {
118 10
            $firstValue = $firstTenLines[$i] ?? null;
119 10
            $secondValue = $secondTenLines[$i] ?? null;
120 10
            if (null !== $firstValue && null !== $secondValue && $firstValue === $secondValue) {
121 10
                return $i + 1;
122
            }
123
        }
124 1
        return 0;
125
    }
126
127
    /**
128
     * @param Iterator<string> $iterator
129
     * @return array<int, string>
130
     */
131 10
    public function splReadTenLines(Iterator $iterator): array
132
    {
133 10
        $result = [];
134 10
        $counter = 0;
135 10
        foreach ($iterator as $line) {
136 10
            $result[] = $this->splCurrentLinesNormalizeValue($line);
137 10
            $counter = $counter + 1;
138 10
            if (10 === $counter) {
139 1
                break;
140
            }
141
        }
142 10
        return $result;
143
    }
144
145 10
    private function splCurrentLinesNormalizeValue(string $current): string
146
    {
147
        // explode values, trim values, remove empty values at end and implode values back
148 10
        return implode(',', array_rtrim(
149 10
            array_map(static fn (?string $value): string => trim($value ?? ''), str_getcsv($current))
150
        ));
151
    }
152
153
    /** @param string[] $searchterms */
154 5
    public function lastLineContains(string $filename, array $searchterms): bool
155
    {
156 5
        $lastline = $this->obtainFileLastLine($filename);
157 5
        foreach ($searchterms as $search) {
158 5
            if (str_contains($lastline, $search)) {
159 5
                return true;
160
            }
161
        }
162
163 5
        return false;
164
    }
165
166 5
    public function obtainFileLastLine(string $filename): string
167
    {
168 5
        $command = sprintf("tail -n 5 %s | grep -v '/^$/' | tail -n 1", escapeshellarg($filename));
169 5
        return shell_exec($command) ?: '';
170
    }
171
}
172