Issues (96)

src/Foundation/FileUpload.php (21 issues)

1
<?php
2
3
namespace Ikechukwukalu\Clamavfileupload\Foundation;
4
5
use Ikechukwukalu\Clamavfileupload\Events\FileDeleteFail;
6
use Ikechukwukalu\Clamavfileupload\Events\FileDeletePass;
7
use Ikechukwukalu\Clamavfileupload\Events\FileForceDeleteFail;
8
use Ikechukwukalu\Clamavfileupload\Events\FileForceDeletePass;
9
use Ikechukwukalu\Clamavfileupload\Events\SavedFilesIntoDB;
10
use Ikechukwukalu\Clamavfileupload\Models\FileUpload as FileUploadModel;
11
use Illuminate\Contracts\Filesystem\Filesystem;
12
use Illuminate\Database\Eloquent\Collection as EloquentCollection;
13
use Illuminate\Http\Request;
14
use Illuminate\Http\UploadedFile;
15
use Illuminate\Support\Str;
16
use Illuminate\Support\Facades\Crypt;
17
use Illuminate\Support\Facades\Log;
18
use Illuminate\Support\Facades\Storage;
19
20
class FileUpload
21
{
22
    public Request $request;
23
    public null|string $ref;
24
    protected array $scanData;
25
    protected bool $hashed;
26
    protected bool $visible;
27
    protected bool $success;
28
    protected null|string $name = null;
29
    protected null|string $errorMessage = null;
30
    protected null|string $folder = null;
31
    protected string $disk;
32
    protected string $fileName;
33
    protected string $input;
34
    protected string $uploadPath = '/';
35
36
    /**
37
     * Log scan data.
38
     *
39
     * @param  string  $message
40
     * @return  void
41
     */
42
    public function logScanData(string $message): void
43
    {
44
        if (config('clamavfileupload.log_scan_data')) {
45
            Log::alert($message);
46
        }
47
    }
48
49
    /**
50
     * Customise file upload settings.
51
     *
52
     * @param  array  $settings
53
     * @return  void
54
     */
55
    public function customFileUploadSettings(array $settings = []): void
56
    {
57
        $whiteList = ['name', 'input', 'folder', 'hashed', 'visible', 'disk'];
58
        $this->uploadPath = '/';
59
60
        foreach ($settings as $key => $setting) {
61
            if (in_array($key, $whiteList)) {
62
                $this->{$key} = $setting;
63
            }
64
        }
65
66
        foreach ($this->defaultFileUploadSettings() as $key => $setting) {
67
            if (!array_key_exists($key, $settings)) {
68
                $this->{$key} = $setting;
69
            }
70
        }
71
72
        if ($this->folder) {
73
            $this->uploadPath .= $this->folder;
74
        }
75
    }
76
77
    /**
78
     * Set fixed file upload settings.
79
     *
80
     * @param  \Illuminate\Http\Request  $request
81
     * @param  null|string  $ref
82
     * @return  void
83
     */
84
    public function fileUploadSettings(Request $request, string $ref = null): void
85
    {
86
        $this->request = $request;
87
        $this->ref = $ref ?? $this->setRef();
88
    }
89
90
    /**
91
     * Get ref.
92
     *
93
     * @return  string
94
     */
95
    public function getRef(): string
96
    {
97
        return $this->ref;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->ref could return the type null which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
98
    }
99
100
    /**
101
     * Get scan data.
102
     *
103
     * @return  string
104
     */
105
    public function getScanData(): array
106
    {
107
        return $this->scanData;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->scanData returns the type array which is incompatible with the documented return type string.
Loading history...
108
    }
109
110
    /**
111
     * Get input.
112
     *
113
     * @return  string
114
     */
115
    public function getInput(): string
116
    {
117
        return $this->input;
118
    }
119
120
    /**
121
     * Check if file upload was successful.
122
     *
123
     * @return  bool
124
     */
125
    public function isSuccessful(): bool
126
    {
127
        return $this->success;
128
    }
129
130
    /**
131
     * Get error message.
132
     *
133
     * @return  null|string
134
     */
135
    public function getErrorMessage(): null|string
136
    {
137
        return $this->errorMessage;
138
    }
139
140
    /**
141
     * Get files.
142
     *
143
     * @param null|string $ref = null
144
     * @param null|string|array $id = null
145
     * @param bool $trashed = false
146
     * @return  FileUploadModel|EloquentCollection
147
     */
148
    public function getFiles(null|string $ref = null, null|string|array $id = null, bool $trashed = false): FileUploadModel|EloquentCollection
149
    {
150
        if (
151
            !($trashed || isset($ref) || isset($id))
152
        ) {
153
            return FileUploadModel::all();
154
        }
155
156
157
        $fileUpload = FileUploadModel::query();
158
        if ($trashed) {
159
            $fileUpload = FileUploadModel::withTrashed(true);
160
        }
161
162
        if (is_string($ref)) {
163
            $fileUpload->where('ref', $ref);
164
        }
165
166
        if (is_array($id)) {
167
            $fileUpload->whereIn('id', $id);
168
        } elseif (isset($id)) {
169
            $fileUpload->where('id', $id);
170
            return $fileUpload->first();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $fileUpload->first() could return the type null which is incompatible with the type-hinted return Ikechukwukalu\Clamavfile...ase\Eloquent\Collection. Consider adding an additional type-check to rule them out.
Loading history...
171
        }
172
173
174
        return $fileUpload->get();
175
    }
176
177
    /**
178
     * Soft delete all files from database by ref.
179
     *
180
     * @param string $ref
181
     * @return bool
182
     */
183
    public function deleteAll(string $ref): bool
184
    {
185
        if (!$response = $this->_deleteAll($ref)) {
186
            return false;
187
        }
188
189
        [$fileUploads, $files] = $response;
190
191
        return $this->_softDeleteFiles($fileUploads, $files);
0 ignored issues
show
It seems like $fileUploads can also be of type Illuminate\Database\Eloq...elations\HasManyThrough and Illuminate\Database\Eloq...Relations\HasOneThrough; however, parameter $fileUploads of Ikechukwukalu\Clamavfile...oad::_softDeleteFiles() does only seem to accept Ikechukwukalu\Clamavfile...ase\Eloquent\Collection, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

191
        return $this->_softDeleteFiles(/** @scrutinizer ignore-type */ $fileUploads, $files);
Loading history...
192
    }
193
194
    /**
195
     * Soft delete multiple files from database by ref and Ids.
196
     *
197
     * @param array $ids
198
     * @param null|string $ref
199
     * @return bool
200
     */
201
    public function deleteMultiple(array $ids, null|string $ref = null): bool
202
    {
203
        if (!$response = $this->_deleteMultiple($ids, $ref)) {
204
            return false;
205
        }
206
207
        [$fileUploads, $files] = $response;
208
209
        return $this->_softDeleteFiles($fileUploads, $files);
210
    }
211
212
    /**
213
     * Soft delete single file from database by ref and id.
214
     *
215
     * @param string $ref
216
     * @param int|string $id
217
     * @return bool
218
     */
219
    public function deleteOne(int|string $id, null|string $ref = null): bool
220
    {
221
        if (!$response = $this->_deleteOne($id, $ref)) {
222
            return false;
223
        }
224
225
        [$fileUpload, $files] = $response;
226
227
        return $this->_softDeleteFiles($fileUpload, $files);
228
    }
229
230
    /**
231
     * Permanently delete all files from directory and database by ref.
232
     *
233
     * @param string $ref
234
     * @return bool
235
     */
236
    public function forceDeleteAll(string $ref): bool
237
    {
238
        if (!$response = $this->_deleteAll($ref)) {
239
            return false;
240
        }
241
242
        [$fileUploads, $files] = $response;
243
        return $this->_forceDeleteFiles($fileUploads, $files);
0 ignored issues
show
It seems like $fileUploads can also be of type Illuminate\Database\Eloq...elations\HasManyThrough and Illuminate\Database\Eloq...Relations\HasOneThrough; however, parameter $fileUploads of Ikechukwukalu\Clamavfile...ad::_forceDeleteFiles() does only seem to accept Ikechukwukalu\Clamavfile...ase\Eloquent\Collection, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

243
        return $this->_forceDeleteFiles(/** @scrutinizer ignore-type */ $fileUploads, $files);
Loading history...
244
    }
245
246
    /**
247
     * Permanently delete multiple files from directory
248
     * and database by ref and Ids.
249
     *
250
     * @param string $ref
251
     * @param array $ids
252
     * @return bool
253
     */
254
    public function forceDeleteMultiple(array $ids, null|string $ref = null): bool
255
    {
256
        if (!$response = $this->_deleteMultiple($ids, $ref)) {
257
            return false;
258
        }
259
260
        [$fileUploads, $files] = $response;
261
262
        return $this->_forceDeleteFiles($fileUploads, $files);
263
    }
264
265
    /**
266
     * Permanently delete single file from directory
267
     * and database by ref and id.
268
     *
269
     * @param string $ref
270
     * @param int|string $id
271
     * @return bool
272
     */
273
    public function forceDeleteOne(int|string $id, null|string $ref = null): bool
274
    {
275
        if (!$response = $this->_deleteOne($id, $ref)) {
276
            return false;
277
        }
278
279
        [$fileUpload, $files] = $response;
280
281
        return $this->_forceDeleteFiles($fileUpload, $files);
282
    }
283
284
    /**
285
     * Save single or multiple files.
286
     *
287
     * @return  bool
288
     * @return  array
289
     */
290
    protected function storeFiles(): bool|array
291
    {
292
        $this->fileName = $this->setFileName();
293
294
        if (is_array($this->request->file($this->input))) {
295
            return $this->saveMultipleFiles();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->saveMultipleFiles() also could return the type array which is incompatible with the documented return type boolean.
Loading history...
296
        }
297
298
        return $this->saveSingleFile();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->saveSingleFile() also could return the type array which is incompatible with the documented return type boolean.
Loading history...
299
    }
300
301
    /**
302
     * Save multiple files.
303
     *
304
     * @param null|string $fileName
305
     * @return  bool
306
     * @return  array
307
     */
308
    protected function saveMultipleFiles(null|string $fileName = null): bool|array
309
    {
310
        return $this->tryCatch(function() use($fileName) {
0 ignored issues
show
The import $fileName is not used and could be removed.

This check looks for imports that have been defined, but are not used in the scope.

Loading history...
311
            $disk = $this->storageDisk();
312
313
            $i = 1;
314
            foreach ($this->request->file($this->input) as $file) {
315
                $fileName = $this->fileName . "_{$i}" . $this->getExtension($file);
316
                $disk->putFileAs($this->folder, $this->encryptFile($file), $fileName);
317
318
                if ($this->visible) {
319
                    $disk->setVisibility($this->folder . "/" . $fileName, 'public');
320
                }
321
322
                $i ++;
323
            }
324
325
            return true;
326
327
        }, function() {
328
            return false;
329
        });
330
    }
331
332
    /**
333
     * Save single file.
334
     *
335
     * @param null|string $fileName
336
     * @return  bool
337
     * @return  array
338
     */
339
    protected function saveSingleFile(null|string $fileName = null): bool|array
340
    {
341
        return $this->tryCatch(function() use($fileName) {
0 ignored issues
show
The import $fileName is not used and could be removed.

This check looks for imports that have been defined, but are not used in the scope.

Loading history...
342
            $fileName = $this->fileName . $this->getExtension();
343
            $file = $this->request->file($this->input);
344
            $disk = $this->storageDisk();
345
346
            $disk->putFileAs($this->uploadPath, $this->encryptFile($file), $fileName);
0 ignored issues
show
It seems like $file can also be of type Illuminate\Http\UploadedFile[] and array and null; however, parameter $file of Ikechukwukalu\Clamavfile...leUpload::encryptFile() does only seem to accept Illuminate\Http\UploadedFile, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

346
            $disk->putFileAs($this->uploadPath, $this->encryptFile(/** @scrutinizer ignore-type */ $file), $fileName);
Loading history...
347
348
            if ($this->visible) {
349
                $disk->setVisibility($this->folder . "/" . $fileName, 'public');
350
            }
351
352
            return true;
353
354
        }, function() {
355
            return false;
356
        });
357
    }
358
359
    /**
360
     * Remove single or multiple files.
361
     *
362
     * @param array $files
363
     * @return  bool
364
     */
365
    protected function removeFiles(array $files = [], null|string $disk = null): bool
366
    {
367
        if ($files !== []) {
368
            return $this->deleteMultipleFiles($files, $disk);
369
        }
370
371
        if (is_array($this->request->file($this->input))) {
372
            return $this->deleteMultipleFiles();
373
        }
374
375
        return $this->deleteSingleFile();
376
    }
377
378
    /**
379
     * Delete multiple files.
380
     *
381
     * @return  bool
382
     */
383
    protected function deleteMultipleFiles(array $files = [], null|string $disk = null): bool
384
    {
385
        if ($files !== []) {
386
            return $this->tryCatch(function() use($files, $disk) {
387
                foreach ($files as $file) {
388
                    Storage::disk($disk)->delete($file);
389
                }
390
391
                return true;
392
393
            }, function() {
394
                return false;
395
            });
396
        }
397
398
        return $this->tryCatch(function() {
399
            $i = 1;
400
            foreach ($this->request->file($this->input) as $file) {
401
                [$fileName, $relativeFilePath] = $this->fileNameAndPath($file, $i);
402
403
                $this->storageDisk()->delete($relativeFilePath);
404
405
                $i ++;
406
            }
407
408
            return true;
409
410
        }, function() {
411
            return false;
412
        });
413
    }
414
415
    /**
416
     * Delete single file.
417
     *
418
     * @return  bool
419
     */
420
    protected function deleteSingleFile(): bool
421
    {
422
        return $this->tryCatch(function() {
423
            [$fileName, $relativeFilePath] = $this->fileNameAndPath();
424
425
            $this->storageDisk()->delete($relativeFilePath);
426
427
            return true;
428
429
        }, function() {
430
            return false;
431
        });
432
    }
433
434
    /**
435
     * Set UUID ref.
436
     *
437
     * @return  string
438
     */
439
    protected function setRef(): string
440
    {
441
        return (string) Str::uuid();
442
    }
443
444
    /**
445
     * Get UUID ref.
446
     *
447
     * @param $file
448
     * @param $i
449
     * @return  string
450
     */
451
    protected function getName($file = null, $i = null): string
452
    {
453
        if ($file && $i) {
454
            return $this->name ? $this->name . "_{$i}"
455
                : $file->getClientOriginalName();
456
        }
457
458
        return $this->name ?? $this->request->file($this->input)
459
                ->getClientOriginalName();
460
    }
461
462
    /**
463
     * Set file name.
464
     *
465
     * @return  string
466
     */
467
    protected function setFileName(): string
468
    {
469
        return time() . Str::random(40);
470
    }
471
472
    /**
473
     * Save file name in database.
474
     *
475
     * @param $file
476
     * @return  string
477
     */
478
    protected function saveFileNameInDB($fileName): string
479
    {
480
        if ($this->hashed) {
481
            return Crypt::encryptString($fileName);
482
        }
483
484
        return $fileName;
485
    }
486
487
    /**
488
     * Save file url in database.
489
     *
490
     * @param $file
491
     * @return  string
492
     */
493
    protected function saveURLInDB($relativeFilePath): string
494
    {
495
        $url = $this->storageDisk()->url($relativeFilePath);
496
497
        if ($this->hashed) {
498
            return Crypt::encryptString($url);
499
        }
500
501
        return $url;
502
    }
503
504
    /**
505
     * Save file path in database.
506
     *
507
     * @param string $relativeFilePath
508
     * @return  string
509
     */
510
    protected function savePathInDB($relativeFilePath): string
511
    {
512
        $path = $this->storageDisk()->path($relativeFilePath);
513
514
        if ($this->hashed) {
515
            return Crypt::encryptString($path);
516
        }
517
518
        return $path;
519
    }
520
521
    /**
522
     * Save file path in database.
523
     *
524
     * @param string $relativeFilePath
525
     * @return  string
526
     */
527
    protected function saveRelativePathInDB($relativeFilePath): string
528
    {
529
        if ($this->hashed) {
530
            return Crypt::encryptString($relativeFilePath);
531
        }
532
533
        return $relativeFilePath;
534
    }
535
536
    /**
537
     * Get extension.
538
     *
539
     * @param $file
540
     * @return  string
541
     */
542
    protected function getExtension($file = null): string
543
    {
544
        if ($file) {
545
            return '.' . $file->getClientOriginalExtension();
546
        }
547
548
        return '.' . $this->request->file($this->input)
549
                ->getClientOriginalExtension();
550
    }
551
552
    /**
553
     * Get relative path.
554
     *
555
     * @param $fileName
556
     * @return  string
557
     */
558
    protected function getRelativeFilePath($fileName): string
559
    {
560
        return $this->folder . "/" . $fileName;
561
    }
562
563
    /**
564
     * Get disk.
565
     *
566
     * @return  string
567
     */
568
    protected function getDisk(): string
569
    {
570
        return $this->disk;
571
    }
572
573
    /**
574
     * Get \Illuminate\Support\Facades\Storage::disk.
575
     *
576
     * @return  \Illuminate\Contracts\Filesystem\Filesystem
577
     */
578
    protected function storageDisk(): Filesystem
579
    {
580
        return Storage::disk($this->getDisk());
581
    }
582
583
    /**
584
     * Get file name and path.
585
     *
586
     * @param $file
587
     * @param $i
588
     * @return  array
589
     */
590
    protected function fileNameAndPath($file = null, $i = null): array
591
    {
592
        if ($file && $i) {
593
            $fileName = $this->fileName . "_{$i}" . $this->getExtension($file);
594
            return [$fileName, $this->getRelativeFilePath($fileName)];
595
        }
596
597
        $fileName = $this->fileName . $this->getExtension();
598
        return [$fileName, $this->getRelativeFilePath($fileName)];
599
    }
600
601
    /**
602
     * Get file model data.
603
     *
604
     * @param $file
605
     * @param $i
606
     * @return  array
607
     */
608
    protected function getFileModelData($file = null, $i = null): array
609
    {
610
        [$fileName, $relativeFilePath] = $this->fileNameAndPath($file, $i);
611
        return [
612
            'ref' => $this->ref,
613
            'name' => $this->getName($file, $i),
614
            'file_name' => $this->saveFileNameInDB($fileName),
615
            'url' => $this->saveURLInDB($relativeFilePath),
616
            'size' => $this->storageDisk()->size($relativeFilePath),
617
            'extension' => $this->getExtension($file),
618
            'disk' => $this->getDisk(),
619
            'mime_type' => $this->storageDisk()->mimeType($relativeFilePath),
620
            'path' => $this->savePathInDB($relativeFilePath),
621
            'relative_path' => $this->saveRelativePathInDB($relativeFilePath),
622
            'folder' => $this->folder,
623
            'hashed' => $this->hashed
624
        ];
625
    }
626
627
    /**
628
     * Insert multiple files.
629
     *
630
     * @return  \Illuminate\Database\Eloquent\Collection
631
     */
632
    protected function insertMultipleFiles(): bool|EloquentCollection
633
    {
634
        $data = [];
635
        $i = 1;
636
637
        foreach ($this->request->file($this->input) as $file) {
638
            [$fileName, $relativeFilePath] = $this->fileNameAndPath($file, $i);
639
640
            if ($this->storageDisk()->missing($relativeFilePath)
641
            ) {
642
                return $this->failedUpload(
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->failedUplo...('name' => $fileName))) returns the type false which is incompatible with the documented return type Illuminate\Database\Eloquent\Collection.
Loading history...
643
                        trans('clamavfileupload::clamav.file_not_found',
644
                        ['name' => $fileName]
645
                    ));
646
            }
647
648
            if (($this->storageDisk()->size($relativeFilePath) < 1)
649
            ) {
650
                $this->removeFiles();
651
                return $this->failedUpload(
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->failedUplo...('name' => $fileName))) returns the type false which is incompatible with the documented return type Illuminate\Database\Eloquent\Collection.
Loading history...
652
                        trans('clamavfileupload::clamav.corrupt_file',
653
                        ['name' => $fileName]
654
                    ));
655
            }
656
657
            $data[] = $this->getFileModelData($file, $i);
658
            $i ++;
659
        }
660
661
        return $this->tryCatch(function() use($data) {
662
            FileUploadModel::insert($data);
663
            $files = FileUploadModel::where('ref', $this->ref)->get();
664
            SavedFilesIntoDB::dispatch($files, $this->ref);
665
            $this->wasUploaded();
666
667
            return $files;
668
669
        }, function() {
670
            return $this->failedUpload(
671
                    trans('clamavfileupload::clamav.database_error',
672
                    ['message' => 'multiple records']
673
            ));
674
        });
675
676
    }
677
678
    /**
679
     * Insert single file.
680
     *
681
     * @return  \Ikechukwukalu\Clamavfileupload\Models\FileUpload
682
     */
683
    protected function insertSingleFile(): bool|FileUploadModel
684
    {
685
        [$fileName, $relativeFilePath] = $this->fileNameAndPath();
686
687
        if ($this->storageDisk()->missing($relativeFilePath))
688
        {
689
            return $this->failedUpload(
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->failedUplo...('name' => $fileName))) returns the type false which is incompatible with the documented return type Ikechukwukalu\Clamavfileupload\Models\FileUpload.
Loading history...
690
                    trans('clamavfileupload::clamav.file_not_found',
691
                    ['name' => $fileName]
692
                ));
693
        }
694
695
        if (($this->storageDisk()->size($relativeFilePath) < 1))
696
        {
697
            $this->removeFiles();
698
            return $this->failedUpload(
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->failedUplo...('name' => $fileName))) returns the type false which is incompatible with the documented return type Ikechukwukalu\Clamavfileupload\Models\FileUpload.
Loading history...
699
                    trans('clamavfileupload::clamav.corrupt_file',
700
                    ['name' => $fileName]
701
                ));
702
        }
703
704
        return $this->tryCatch(function() use ($fileName, $relativeFilePath) {
0 ignored issues
show
The import $fileName is not used and could be removed.

This check looks for imports that have been defined, but are not used in the scope.

Loading history...
The import $relativeFilePath is not used and could be removed.

This check looks for imports that have been defined, but are not used in the scope.

Loading history...
705
            $file = FileUploadModel::create($this->getFileModelData());
706
            SavedFilesIntoDB::dispatch($file, $this->ref);
707
            $this->wasUploaded();
708
709
            return $file;
710
711
        }, function() {
712
            return $this->failedUpload(
713
                trans('clamavfileupload::clamav.database_error',
714
                ['message' => 'single record']
715
            ));
716
        });
717
    }
718
719
    /**
720
     * Default file upload settings.
721
     *
722
     * @return  array
723
     */
724
    protected function defaultFileUploadSettings(): array
725
    {
726
        return [
727
            'name' => null,
728
            'input' => config('clamavfileupload.input', 'file'),
729
            'folder' => null,
730
            'hashed' => config('clamavfileupload.hashed', false),
731
            'visible' => config('clamavfileupload.visible', true),
732
            'disk' => config('clamavfileupload.disk', 'local')
733
        ];
734
    }
735
736
    /**
737
     * Set that files failed to upload.
738
     *
739
     * @param string $message
740
     * @return bool
741
     */
742
    protected function failedUpload(string $message): bool
743
    {
744
        $this->errorMessage = $message;
745
        $this->success = false;
746
747
        return false;
748
    }
749
750
    /**
751
     * Set that files failed to delete.
752
     *
753
     * @param string $message
754
     * @return bool
755
     */
756
    protected function failedDelete(string $message): bool
757
    {
758
        return $this->failedUpload($message);
759
    }
760
761
    /**
762
     * Set that files was uploaded.
763
     *
764
     * @return bool
765
     */
766
    protected function wasUploaded(): bool
767
    {
768
        $this->success = true;
769
770
        return true;
771
    }
772
773
    /**
774
     * Try catch block.
775
     *
776
     * @param callable $tryFunc
777
     * @param callable $catchFunc
778
     * @return mixed
779
     */
780
    protected function tryCatch(callable $tryFunc, callable $catchFunc): mixed
781
    {
782
        try {
783
            return $tryFunc();
784
        } catch (\Throwable $th) {
785
            $this->failedUpload($th->getMessage());
786
            Log::error($th->getMessage());
787
            return $catchFunc();
788
        }
789
    }
790
791
    /**
792
     *
793
     * @param \Ikechukwukalu\Clamavfileupload\Models\FileUpload|\Illuminate\Database\Eloquent\Collection $fileUploads
794
     * @param array $files
795
     * @return bool
796
     */
797
    public function _softDeleteFiles(EloquentCollection|FileUploadModel $fileUploads, array $files): bool
798
    {
799
        return $this->tryCatch(function() use($fileUploads, $files) {
800
            if ($fileUploads instanceof EloquentCollection) {
801
                $fileUploads->each->delete();
802
                return true;
803
            }
804
805
            $fileUploads->delete();
806
            FileDeletePass::dispatch($files);
807
808
            return true;
809
810
        }, function() use($files) {
811
            FileDeleteFail::dispatch($files);
812
            return false;
813
        });
814
    }
815
816
    /**
817
     *
818
     * @param \Ikechukwukalu\Clamavfileupload\Models\FileUpload|\Illuminate\Database\Eloquent\Collection $fileUploads
819
     * @param array $files
820
     * @return bool
821
     */
822
    public function _forceDeleteFiles(EloquentCollection|FileUploadModel $fileUploads, array $files): bool
823
    {
824
        return $this->tryCatch(function() use($fileUploads, $files) {
825
            if ($fileUploads instanceof EloquentCollection) {
826
                $disk = $fileUploads[0]->disk;
827
                $fileUploads->each->forceDelete();
828
                $this->removeFiles($files, $disk);
829
830
                return true;
831
            }
832
833
            $disk = $fileUploads->disk;
834
            $fileUploads->forceDelete();
835
            $this->removeFiles($files, $disk);
836
            FileForceDeletePass::dispatch($files);
837
838
            return true;
839
840
        }, function() use($files) {
841
            FileForceDeleteFail::dispatch($files);
842
            return false;
843
        });
844
    }
845
846
    /**
847
     *
848
     * @param \Ikechukwukalu\Clamavfileupload\Models\FileUpload|\Illuminate\Database\Eloquent\Collection $fileUploads
849
     * @param array $files
850
     * @return array
851
     */
852
    public function extractPath(EloquentCollection|FileUploadModel $fileUploads): array
853
    {
854
        if ($fileUploads instanceof FileUploadModel) {
855
            return [$fileUploads->relative_path];
856
        }
857
858
        return $fileUploads->pluck('relative_path')->toArray();
859
    }
860
861
    /**
862
     * Soft delete all files from database by ref.
863
     *
864
     * @param string $ref
865
     * @return bool
866
     */
867
    private function _deleteAll(string $ref): array|null
868
    {
869
        $fileUploads = FileUploadModel::where('ref', $ref)->get();
870
871
        if ($fileUploads->count() < 1) {
872
            $this->failedDelete(
873
                trans('clamavfileupload::clamav.files_not_found_in_db'));
874
            return null;
875
        }
876
877
        $files = $this->extractPath($fileUploads);
0 ignored issues
show
It seems like $fileUploads can also be of type Illuminate\Database\Eloq...elations\HasManyThrough and Illuminate\Database\Eloq...Relations\HasOneThrough; however, parameter $fileUploads of Ikechukwukalu\Clamavfile...leUpload::extractPath() does only seem to accept Ikechukwukalu\Clamavfile...ase\Eloquent\Collection, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

877
        $files = $this->extractPath(/** @scrutinizer ignore-type */ $fileUploads);
Loading history...
878
879
        return [$fileUploads, $files];
880
    }
881
882
    /**
883
     * Soft delete multiple files from database by ref and Ids.
884
     *
885
     * @param array $ids
886
     * @param null|string $ref
887
     * @return bool
888
     */
889
    private function _deleteMultiple(array $ids, null|string $ref = null): array|null
890
    {
891
        $query = FileUploadModel::query();
892
        if ($ref) {
893
            $query->where('ref', $ref);
894
        }
895
896
        $fileUploads = $query->whereIn('id', $ids)->get();
897
        if ($fileUploads->count() < 1) {
898
            $this->failedDelete(
899
                trans('clamavfileupload::clamav.files_not_found_in_db'));
900
            return null;
901
        }
902
903
        $files = $this->extractPath($fileUploads);
904
905
        return [$fileUploads, $files];
0 ignored issues
show
Bug Best Practice introduced by
The expression return array($fileUploads, $files) returns the type array<integer,Illuminate...<integer,string>|mixed> which is incompatible with the documented return type boolean.
Loading history...
906
    }
907
908
    /**
909
     * Soft delete single file from database by ref and id.
910
     *
911
     * @param string $ref
912
     * @param int|string $id
913
     * @return bool
914
     */
915
    private function _deleteOne(int|string $id, null|string $ref = null): array|null
916
    {
917
        $query = FileUploadModel::query();
918
        if ($ref) {
919
            $query->where('ref', $ref);
920
        }
921
922
        if (!$fileUpload = $query->where('id', $id)->first()) {
923
            $this->failedDelete(
924
                trans('clamavfileupload::clamav.files_not_found_in_db'));
925
            return null;
926
        }
927
928
        $files = $this->extractPath($fileUpload);
929
930
        return [$fileUpload, $files];
0 ignored issues
show
Bug Best Practice introduced by
The expression return array($fileUpload, $files) returns the type array<integer,Ikechukwuk...|array<integer,string>> which is incompatible with the documented return type boolean.
Loading history...
931
    }
932
933
    /**
934
     * EncryptFile.
935
     *
936
     * @param \Illuminate\Http\UploadedFile $file
937
     * @return void
938
     */
939
    private function encryptFile(UploadedFile $file): string|UploadedFile
940
    {
941
        if ($this->hashed) {
942
            return Crypt::encrypt($file->getContent());
0 ignored issues
show
Bug Best Practice introduced by
The expression return Illuminate\Suppor...pt($file->getContent()) returns the type string which is incompatible with the documented return type void.
Loading history...
943
        }
944
945
        return $file;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $file returns the type Illuminate\Http\UploadedFile which is incompatible with the documented return type void.
Loading history...
946
    }
947
948
}
949