Passed
Push — master ( c090bb...e5cdcf )
by Carlos C
01:54 queued 12s
created

CsvFolderJoinFiles::splReadTenLines()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 3

Importance

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