Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
0.00% |
0 / 1 |
|
72.73% |
8 / 11 |
CRAP | |
91.14% |
72 / 79 |
HandleFile | |
0.00% |
0 / 1 |
|
72.73% |
8 / 11 |
28.55 | |
91.14% |
72 / 79 |
__construct | |
100.00% |
1 / 1 |
1 | |
100.00% |
2 / 2 |
|||
config | |
100.00% |
1 / 1 |
1 | |
100.00% |
4 / 4 |
|||
store | |
100.00% |
1 / 1 |
5 | |
100.00% |
13 / 13 |
|||
setFile | |
0.00% |
0 / 1 |
3.21 | |
71.43% |
5 / 7 |
|||
storage | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
readZip | |
0.00% |
0 / 1 |
3.33 | |
66.67% |
6 / 9 |
|||
isValid | |
100.00% |
1 / 1 |
3 | |
100.00% |
9 / 9 |
|||
count | |
100.00% |
1 / 1 |
3 | |
100.00% |
7 / 7 |
|||
rows | |
100.00% |
1 / 1 |
2 | |
100.00% |
4 / 4 |
|||
unzip | |
0.00% |
0 / 1 |
3.07 | |
80.00% |
8 / 10 |
|||
normalizeFile | |
100.00% |
1 / 1 |
3 | |
100.00% |
13 / 13 |
1 | <?php |
2 | |
3 | namespace Qmp\Laravel\Deduplication\Objects; |
4 | |
5 | use Illuminate\Http\UploadedFile; |
6 | use Illuminate\Support\Facades\{Storage, Validator}; |
7 | use League\Flysystem\Filesystem; |
8 | use League\Flysystem\ZipArchive\ZipArchiveAdapter; |
9 | use Qmp\Laravel\Deduplication\Exceptions\ZipErrorException; |
10 | use Qmp\Laravel\Deduplication\Rules\ListDedupRule; |
11 | |
12 | class 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 | } |