FileUpload::insertSingleFile()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 32
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 19
c 2
b 0
f 0
dl 0
loc 32
rs 9.6333
cc 3
nc 3
nop 0
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
Bug introduced by
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
Bug introduced by
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
Unused Code introduced by
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
Unused Code introduced by
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
Bug introduced by
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
Unused Code introduced by
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...
Unused Code introduced by
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
Bug introduced by
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