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 | } |