Passed
Pull Request — master (#3)
by Carlos C
12:19 queued 41s
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
    public function joinFilesToDestination(array $files, string $destination): void
25 5
    {
26
        $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 5
            }
33
            $skipLastLines = 0;
34 5
            if ($this->lastLineContains($source, ['Continúa en'])) {
35 5
                $skipLastLines = 1;
36 5
            }
37
            $this->writeLines($source, $destination, $skipFirstLines, $skipLastLines);
38 5
        }
39
    }
40 5
41
    /**
42
     * @return array<string, array<int, string>>
43
     */
44
    public function obtainFilesThatAreSplitted(string $csvFolder): array
45
    {
46 6
        $files = array_filter(
47
            array_map(
48 6
                function ($path): array {
49 6
                    $file = basename($path);
50 6
                    $matches = [];
51 6
                    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 6
                    ) {
56
                        return [];
57 4
                    }
58
                    return [
59
                        'destination' => dirname($path) . '/' . trim($matches[1]) . '.csv',
60 6
                        'index' => (int) $matches[2],
61 6
                        'source' => $path,
62 6
                    ];
63
                },
64 6
                glob($csvFolder . '/*.csv') ?: []
65 6
            )
66
        );
67
68
        uasort($files, [$this, 'compareFiles']);
69 6
70 6
        $destinations = [];
71 6
        foreach ($files as $file) {
72
            $destinations[strval($file['destination'])][] = strval($file['source']);
73 6
        }
74 6
75 6
        return $destinations;
76
    }
77
78 6
    /**
79
     * @param array{destination: string, index: int} $first
80
     * @param array{destination: string, index: int} $second
81 5
     */
82
    private function compareFiles(array $first, array $second): int
83 5
    {
84 5
        return ($first['destination'] <=> $second['destination']) ?: $first['index'] <=> $second['index'];
85 5
    }
86 5
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
            sprintf('| tail -n +%d', $skipFirstLines + 1),      // without firsts n lines
92 6
            sprintf('| head -n -%d', $skipLastLines),           // without last n lines
93
            '>> ' . escapeshellarg($destination),                // create/append to destination
94 6
        ]);
95 6
        shell_exec($command);
96 6
    }
97 6
98 6
    public function findLinesToSkip(string $firstPath, string $secondPath): int
99 6
    {
100 6
        $lines = 0;
101
        $first = new SplFileObject($firstPath, 'r');
102 6
        $second = new SplFileObject($secondPath, 'r');
103
        while ($this->splCurrentLinesAreEqual($first, $second)) {
104
            $first->next();
105 6
            $second->next();
106
            $lines = $lines + 1;
107 6
        }
108 1
        return $lines;
109
    }
110 6
111 6
    /**
112 6
     * @param Iterator<mixed> $first
113
     * @param Iterator<mixed> $second
114
     */
115
    private function splCurrentLinesAreEqual(Iterator $first, Iterator $second): bool
116
    {
117
        if (! $first->valid() || ! $second->valid()) {
118
            return false;
119 6
        }
120
        $firstValue = $this->splCurrentLinesAreEqualNormalizeValue($first->current());
121 6
        $secondValue = $this->splCurrentLinesAreEqualNormalizeValue($second->current());
122
        return ($firstValue === $secondValue);
123
    }
124 6
125
    private function splCurrentLinesAreEqualNormalizeValue(mixed $current): mixed
126
    {
127
        if (! is_string($current)) {
128
            return $current;
129
        }
130
        return trim(implode(',', array_map('trim', explode(',', rtrim($current, ',')))));
131
    }
132 5
133
    /**
134 5
     * @param string[] $searchterms
135 5
     */
136 5
    public function lastLineContains(string $filename, array $searchterms): bool
137 5
    {
138
        $lastline = $this->obtainFileLastLine($filename);
139
        foreach ($searchterms as $search) {
140
            if (str_contains($lastline, $search)) {
141 5
                return true;
142
            }
143
        }
144 5
145
        return false;
146 5
    }
147 5
148
    public function obtainFileLastLine(string $filename): string
149
    {
150
        $command = sprintf("tail -n 5 %s | grep -v '/^$/' | tail -n 1", escapeshellarg($filename));
151
        return shell_exec($command) ?: '';
152
    }
153
}
154