Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
72.73% covered (success)
72.73%
8 / 11
CRAP
91.14% covered (success)
91.14%
72 / 79
HandleFile
0.00% covered (danger)
0.00%
0 / 1
72.73% covered (success)
72.73%
8 / 11
28.55
91.14% covered (success)
91.14%
72 / 79
 __construct
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
2 / 2
 config
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
4 / 4
 store
100.00% covered (success)
100.00%
1 / 1
5
100.00% covered (success)
100.00%
13 / 13
 setFile
0.00% covered (danger)
0.00%
0 / 1
3.21
71.43% covered (success)
71.43%
5 / 7
 storage
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 readZip
0.00% covered (danger)
0.00%
0 / 1
3.33
66.67% covered (warning)
66.67%
6 / 9
 isValid
100.00% covered (success)
100.00%
1 / 1
3
100.00% covered (success)
100.00%
9 / 9
 count
100.00% covered (success)
100.00%
1 / 1
3
100.00% covered (success)
100.00%
7 / 7
 rows
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
4 / 4
 unzip
0.00% covered (danger)
0.00%
0 / 1
3.07
80.00% covered (success)
80.00%
8 / 10
 normalizeFile
100.00% covered (success)
100.00%
1 / 1
3
100.00% covered (success)
100.00%
13 / 13
1<?php
2
3namespace Qmp\Laravel\Deduplication\Objects;
4
5use Illuminate\Http\UploadedFile;
6use Illuminate\Support\Facades\{Storage, Validator};
7use League\Flysystem\Filesystem;
8use League\Flysystem\ZipArchive\ZipArchiveAdapter;
9use Qmp\Laravel\Deduplication\Exceptions\ZipErrorException;
10use Qmp\Laravel\Deduplication\Rules\ListDedupRule;
11
12class HandleFile
13{
14    protected $disk;
15
16    protected $filesystem;
17
18    protected $hasType;
19
20    protected $uploadedFile;
21
22    protected $file;
23
24    /**
25     * Undocumented variable
26     *
27     * @var array
28     */
29    protected $object = [
30        'base' => null,
31        'errors' => null,
32        'file' => null,
33        'isZip' => false,
34        'count' => 0,
35        'uploaded_at' => null,
36    ];
37
38    /**
39     * Undocumented variable
40     *
41     * @var integer
42     */
43    protected $numberLinesValidate = 3;
44
45    /**
46     * Undocumented variable
47     *
48     * @var array
49     */
50    protected $normalization = [
51        'sed -i \'1s/^\xEF\xBB\xBF//\' %s',
52        'cat %s | tr \'\r\' \'\n\' | tr -s \'\n\' | tr -d \'[,;]\' > %s',
53    ];
54
55    /**
56     * HandleFile constructor.
57     */
58    public function __construct()
59    {
60        $this->disk = Storage::disk('deduplication');
61    }
62
63    /**
64     * @param UploadedFile $file
65     * @param string $hashType
66     * @return $this
67     */
68    public function config(UploadedFile $file, string $hashType)
69    {
70        $this->hastype = $hashType;
71        $this->uploadedFile = $file;
72        $this->setFile($file);
73
74        return $this;
75    }
76
77    /**
78     * @param string $subFolder
79     * @param string $basename
80     * @return array
81     * @throws \Exception
82     */
83    public function store(string $subFolder, string $basename)
84    {
85        $this->object['uploaded_at'] = date('Y-m-d H:i:s');
86
87        $filename = $this->storage($subFolder, $basename);
88        $this->object['base'] = $filename;
89
90        if (is_null($this->object['errors'])) {
91            $this->object['file'] = $this->file instanceof UploadedFile ? $filename : $subFolder . '/' . $this->file['path'];
92        }
93
94        if ($this->object['isZip']) {
95            $this->unzip();
96        }
97
98        $this->normalizeFile();
99
100        $errors = $this->isValid();
101        $this->object['count'] = $this->count();
102
103        if (!is_null($errors)) {
104            $this->object['errors'] = $errors;
105        }
106
107        return $this->object;
108    }
109
110    /**
111     * @param UploadedFile $file
112     */
113    private function setFile(UploadedFile $file)
114    {
115        try {
116            if (strtolower($file->extension()) !== 'zip') {
117                $this->file = $file;
118            } else {
119                $this->readZip($file);
120                $this->object['isZip'] = true;
121            }
122        } catch (\Exception $e) {
123            $this->object['errors'] = $e->getMessage();
124        }
125    }
126
127    /**
128     * @param string $subFolder
129     * @param string $basename
130     * @return false|string
131     */
132    private function storage(string $subFolder, string $basename)
133    {
134        return $this->disk->putFileAs($subFolder, $this->uploadedFile, $basename);
135    }
136
137    /**
138     * @param UploadedFile $file
139     * @throws \Exception
140     */
141    private function readZip(UploadedFile $file)
142    {
143        try {
144            $this->filesystem = new Filesystem(new ZipArchiveAdapter($file));
145            $contents = collect($this->filesystem->listContents('', true));
146            $filtered = $contents->where('type', 'file');
147
148            if ($filtered->count() !== 1) {
149                $this->object['errors'] = 'Several files in the zip archive';
150            }
151
152            $this->file = $filtered->first();
153        } catch (\Exception $e) {
154            $this->object['errors'] = $e->getMessage();
155        }
156    }
157
158    /**
159     * @return string|null
160     */
161    private function isValid()
162    {
163        $ressource = fopen($this->disk->path($this->object['file']), 'r');
164        $cursor = 0;
165
166        do {
167            $line = trim(fgets($ressource));
168
169            $validator = Validator::make(['line' => $line], ['line' => new ListDedupRule($this->hastype)]);
170            $errors = $validator->errors();
171            $cursor++;
172        } while (!empty($errors->first('line') && $cursor < $this->numberLinesValidate));
173
174        fclose($ressource);
175
176        return !empty($errors->first('line')) ? $errors->first('line') : null;
177    }
178
179    /**
180     * @return int
181     */
182    private function count(): int
183    {
184        $ressource = fopen($this->disk->path($this->object['file']), 'r');
185
186        $count = 0;
187        foreach ($this->rows($ressource) as $row) {
188            if (!empty($row)) {
189                $count++;
190            }
191        }
192
193        fclose($ressource);
194        return $count;
195    }
196
197    /**
198     * @param $ressource
199     * @return \Generator
200     */
201    private function rows($ressource): \Generator
202    {
203        while (!feof($ressource)) {
204            yield trim(fgets($ressource));
205        }
206    }
207
208    /**
209     *
210     */
211    private function unzip()
212    {
213        try {
214            $zipFile = $this->disk->path($this->object['base']);
215            $path = pathinfo($zipFile, PATHINFO_DIRNAME);
216            $zip = new \ZipArchive;
217
218            if ($zip->open($zipFile) === TRUE) {
219                $zip->extractTo($path);
220                $zip->close();
221            } else {
222                throw new ZipErrorException("Failed to extract zip " . $zipFile);
223            }
224        } catch (\Exception $e) {
225            $this->object['errors'] = $e->getMessage();
226        }
227    }
228
229    /**
230     * Undocumented function
231     *
232     * @return void
233     */
234    protected function normalizeFile()
235    {
236        $path = $this->disk->path($this->object['file']);
237        $basename = pathinfo($path, PATHINFO_BASENAME);
238        $filename = pathinfo($path, PATHINFO_FILENAME);
239
240        $normalizedBasename = str_replace($filename, "$filename-normalized", $basename);
241        $normalizedFilename = str_replace($basename, $normalizedBasename, $this->object['file']);
242        $normalizedPath = $this->disk->path($normalizedFilename);
243
244        foreach ($this->normalization as $norm) {
245            $norm = sprintf($norm, $path, $normalizedPath);
246            exec($norm);
247        }
248
249        if (file_exists($normalizedPath)) {
250            unlink($path);
251            rename($normalizedPath, $path);
252        }
253    }
254}