Passed
Push — master ( d96a4c...76b07c )
by Carlos C
05:39
created

CsvFolderJoinFiles::obtainFileLastLine()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

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