Passed
Push — master ( bc2c58...de2b0e )
by Carlos C
02:23 queued 12s
created

CsvFolderJoinFiles::compareFiles()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 1
c 0
b 0
f 0
nc 1
nop 2
dl 0
loc 3
rs 10
ccs 2
cts 2
cp 1
crap 2
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 4
    }
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 5
    }
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 6
                        'source' => $path,
62
                    ];
63 6
                },
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
     */
82 6
    private function compareFiles(array $first, array $second): int
83
    {
84 6
        return ($first['destination'] <=> $second['destination']) ?: $first['index'] <=> $second['index'];
85
    }
86
87 5
    public function writeLines(string $source, string $destination, int $skipFirstLines, int $skipLastLines): void
88
    {
89 5
        $command = implode(' ', [
90 5
            'cat ' . escapeshellarg($source),                   // send the file to the pipes
91 5
            sprintf('| tail -n +%d', $skipFirstLines + 1),      // without firsts n lines
92 5
            sprintf('| head -n -%d', $skipLastLines),           // without last n lines
93 5
            '>> ' . escapeshellarg($destination),                // create/append to destination
94
        ]);
95 5
        shell_exec($command);
96 5
    }
97
98 6
    public function findLinesToSkip(string $firstPath, string $secondPath): int
99
    {
100 6
        $lines = 0;
101 6
        $first = new SplFileObject($firstPath, 'r');
102 6
        $second = new SplFileObject($secondPath, 'r');
103 6
        while ($this->splCurrentLinesAreEqual($first, $second)) {
104 6
            $first->next();
105 6
            $second->next();
106 6
            $lines = $lines + 1;
107
        }
108 6
        return $lines;
109
    }
110
111
    /**
112
     * @param Iterator<mixed> $first
113
     * @param Iterator<mixed> $second
114
     */
115 6
    private function splCurrentLinesAreEqual(Iterator $first, Iterator $second): bool
116
    {
117 6
        if (! $first->valid() || ! $second->valid()) {
118 1
            return false;
119
        }
120 6
        $firstValue = $this->splCurrentLinesAreEqualNormalizeValue($first->current());
121 6
        $secondValue = $this->splCurrentLinesAreEqualNormalizeValue($second->current());
122 6
        return ($firstValue === $secondValue);
123
    }
124
125 6
    private function splCurrentLinesAreEqualNormalizeValue(mixed $current): mixed
126
    {
127 6
        if (! is_string($current)) {
128
            return $current;
129
        }
130 6
        return trim(implode(',', array_map('trim', explode(',', rtrim($current, ',')))));
131
    }
132
133
    /**
134
     * @param string[] $searchterms
135
     */
136 5
    public function lastLineContains(string $filename, array $searchterms): bool
137
    {
138 5
        $lastline = $this->obtainFileLastLine($filename);
139 5
        foreach ($searchterms as $search) {
140 5
            if (str_contains($lastline, $search)) {
141 5
                return true;
142
            }
143
        }
144
145 5
        return false;
146
    }
147
148 5
    public function obtainFileLastLine(string $filename): string
149
    {
150 5
        $command = sprintf("tail -n 5 %s | grep -v '/^$/' | tail -n 1", escapeshellarg($filename));
151 5
        return shell_exec($command) ?: '';
152
    }
153
}
154