Scrutinizer GitHub App not installed

We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.

Install GitHub App

Test Failed
Pull Request — main (#5518)
by Pedro
34:25 queued 23:25
created

Uploader::deleteFiles()   B

Complexity

Conditions 9
Paths 19

Size

Total Lines 33
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 6
Bugs 4 Features 1
Metric Value
cc 9
eloc 18
c 6
b 4
f 1
nc 19
nop 1
dl 0
loc 33
rs 8.0555
1
<?php
2
3
namespace Backpack\CRUD\app\Library\Uploaders;
4
5
use Backpack\CRUD\app\Library\Uploaders\Support\Interfaces\UploaderInterface;
6
use Backpack\CRUD\app\Library\Uploaders\Support\Traits\HandleFileNaming;
7
use Backpack\CRUD\app\Library\Uploaders\Support\Traits\HandleRepeatableUploads;
8
use Illuminate\Database\Eloquent\Model;
9
use Illuminate\Database\Eloquent\SoftDeletes;
10
use Illuminate\Support\Facades\Storage;
11
use Illuminate\Support\Str;
12
13
abstract class Uploader implements UploaderInterface
14
{
15
    use HandleFileNaming;
16
    use HandleRepeatableUploads;
0 ignored issues
show
introduced by
The trait Backpack\CRUD\app\Librar...HandleRepeatableUploads requires some properties which are not provided by Backpack\CRUD\app\Library\Uploaders\Uploader: $pivot, $exists, $pivotParent
Loading history...
17
18
    private string $name;
19
20
    private string $disk = 'public';
21
22
    private string $path = '';
23
24
    private bool $handleMultipleFiles = false;
25
26
    private bool $deleteWhenEntryIsDeleted = true;
27
28
    private bool|string $attachedToFakeField = false;
29
30
    /**
31
     * Cloud disks have the ability to generate temporary URLs to files, should we do it?
32
     */
33
    private bool $useTemporaryUrl = false;
34
35
    /**
36
     * When using temporary urls, define the time that the url will be valid.
37
     */
38
    private int $temporaryUrlExpirationTimeInMinutes = 1;
39
40
    /**
41
     * Indicates if the upload is relative to a relationship field/column.
42
     */
43
    private bool $isRelationship = false;
44
45
    /**
46
     * When previous files are updated, we need to keep track of them so that we don't add deleted files to the new list.
47
     */
48
    private $updatedPreviousFiles = null;
49
50
    public function __construct(array $crudObject, array $configuration)
51
    {
52
        $this->name = $crudObject['name'];
53
        $this->disk = $configuration['disk'] ?? $crudObject['disk'] ?? $this->disk;
54
        $this->path = $this->getPathFromConfiguration($crudObject, $configuration);
55
        $this->attachedToFakeField = isset($crudObject['fake']) && $crudObject['fake'] ? ($crudObject['store_in'] ?? 'extras') : ($crudObject['store_in'] ?? false);
56
        $this->useTemporaryUrl = $configuration['temporaryUrl'] ?? $this->useTemporaryUrl;
57
        $this->temporaryUrlExpirationTimeInMinutes = $configuration['temporaryUrlExpirationTime'] ?? $this->temporaryUrlExpirationTimeInMinutes;
58
        $this->deleteWhenEntryIsDeleted = $configuration['deleteWhenEntryIsDeleted'] ?? $this->deleteWhenEntryIsDeleted;
59
        $this->fileNamer = is_callable($configuration['fileNamer'] ?? null) ? $configuration['fileNamer'] : $this->getFileNameGeneratorInstance($configuration['fileNamer'] ?? null);
60
    }
61
62
    /*******************************
63
     * Static methods
64
     *******************************/
65
    public static function for(array $crudObject, array $definition): UploaderInterface
66
    {
67
        return new static($crudObject, $definition);
68
    }
69
70
    /*******************************
71
     * public methods - event handler methods
72
     *******************************/
73
    public function storeUploadedFiles(Model $entry): Model
74
    {
75
        if ($this->handleRepeatableFiles) {
76
            return $this->handleRepeatableFiles($entry);
77
        }
78
79
        if ($this->attachedToFakeField) {
80
            $fakeFieldValue = $entry->{$this->attachedToFakeField};
81
            $fakeFieldValue = is_string($fakeFieldValue) ? json_decode($fakeFieldValue, true) : (array) $fakeFieldValue;
82
            $fakeFieldValue[$this->getAttributeName()] = $this->uploadFiles($entry);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $fakeFieldValue[$this->getAttributeName()] is correct as $this->uploadFiles($entry) targeting Backpack\CRUD\app\Librar...Uploader::uploadFiles() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
83
84
            $entry->{$this->attachedToFakeField} = isset($entry->getCasts()[$this->attachedToFakeField]) ? $fakeFieldValue : json_encode($fakeFieldValue);
85
86
            return $entry;
87
        }
88
89
        $entry->{$this->getAttributeName()} = $this->uploadFiles($entry);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $entry->$this->getAttributeName() is correct as $this->uploadFiles($entry) targeting Backpack\CRUD\app\Librar...Uploader::uploadFiles() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
90
91
        return $entry;
92
    }
93
94
    public function retrieveUploadedFiles(Model $entry): Model
95
    {
96
        if ($this->handleRepeatableFiles) {
97
            return $this->retrieveRepeatableFiles($entry);
98
        }
99
100
        return $this->retrieveFiles($entry);
101
    }
102
103
    public function deleteUploadedFiles(Model $entry): void
104
    {
105
        if ($this->deleteWhenEntryIsDeleted) {
106
            if (! in_array(SoftDeletes::class, class_uses_recursive($entry), true)) {
107
                $this->performFileDeletion($entry);
108
109
                return;
110
            }
111
112
            if ($entry->isForceDeleting() === true) {
113
                $this->performFileDeletion($entry);
114
            }
115
        }
116
    }
117
118
    /*******************************
119
     * Getters
120
     *******************************/
121
    public function getName(): string
122
    {
123
        return $this->name;
124
    }
125
126
    public function getAttributeName(): string
127
    {
128
        return Str::afterLast($this->name, '.');
129
    }
130
131
    public function getDisk(): string
132
    {
133
        return $this->disk;
134
    }
135
136
    public function getPath(): string
137
    {
138
        return $this->path;
139
    }
140
141
    public function useTemporaryUrl(): bool
142
    {
143
        return $this->useTemporaryUrl;
144
    }
145
146
    public function getExpirationTimeInMinutes(): int
147
    {
148
        return $this->temporaryUrlExpirationTimeInMinutes;
149
    }
150
151
    public function shouldDeleteFiles(): bool
152
    {
153
        return $this->deleteWhenEntryIsDeleted;
154
    }
155
156
    public function getIdentifier(): string
157
    {
158
        if ($this->handleRepeatableFiles) {
159
            return $this->repeatableContainerName.'_'.$this->name;
160
        }
161
162
        return $this->name;
163
    }
164
165
    public function getNameForRequest(): string
166
    {
167
        return $this->repeatableContainerName ?? $this->name;
168
    }
169
170
    public function canHandleMultipleFiles(): bool
171
    {
172
        return $this->handleMultipleFiles;
173
    }
174
175
    public function isRelationship(): bool
176
    {
177
        return $this->isRelationship;
178
    }
179
180
    public function getPreviousFiles(Model $entry): mixed
181
    {
182
        if (! $this->attachedToFakeField) {
183
            return $this->getOriginalValue($entry);
184
        }
185
186
        $value = $this->getOriginalValue($entry, $this->attachedToFakeField);
187
        $value = is_string($value) ? json_decode($value, true) : (array) $value;
188
189
        return $value[$this->getAttributeName()] ?? null;
190
    }
191
192
    public function getValueWithoutPath(?string $value = null): ?string
193
    {
194
        return $value ? Str::after($value, $this->path) : null;
195
    }
196
197
    public function isFake(): bool
198
    {
199
        return $this->attachedToFakeField !== false;
200
    }
201
202
    public function getFakeAttribute(): bool|string
203
    {
204
        return $this->attachedToFakeField;
205
    }
206
207
    /*******************************
208
     * Setters - fluently configure the uploader
209
     *******************************/
210
    public function multiple(): self
211
    {
212
        $this->handleMultipleFiles = true;
213
214
        return $this;
215
    }
216
217
    public function relationship(bool $isRelationship): self
218
    {
219
        $this->isRelationship = $isRelationship;
220
221
        return $this;
222
    }
223
224
    public function fake(bool|string $isFake): self
225
    {
226
        $this->attachedToFakeField = $isFake;
227
228
        return $this;
229
    }
230
231
    /*******************************
232
     * Default implementation functions
233
     *******************************/
234
    public function uploadFiles(Model $entry, $values = null)
235
    {
236
    }
237
238
    private function retrieveFiles(Model $entry): Model
239
    {
240
        $value = $entry->{$this->getAttributeName()};
241
242
        if ($this->attachedToFakeField) {
243
            $values = $entry->{$this->attachedToFakeField};
244
245
            $values = is_string($values) ? json_decode($values, true) : $values;
246
247
            $values[$this->getAttributeName()] = isset($values[$this->getAttributeName()]) ? $this->getValueWithoutPath($values[$this->getAttributeName()]) : null;
248
            $entry->{$this->attachedToFakeField} = isset($entry->getCasts()[$this->attachedToFakeField]) ? $values : json_encode($values);
249
250
            return $entry;
251
        }
252
253
        if ($this->handleMultipleFiles) {
254
            if (! isset($entry->getCasts()[$this->getName()]) && is_string($value)) {
255
                $entry->{$this->getAttributeName()} = json_decode($value, true);
256
            }
257
258
            return $entry;
259
        }
260
261
        $entry->{$this->getAttributeName()} = $this->getValueWithoutPath($value);
262
263
        return $entry;
264
    }
265
266
    private function deleteFiles(Model $entry)
267
    {
268
        if (! $this->shouldDeleteFiles()) {
269
            return;
270
        }
271
272
        if ($this->attachedToFakeField) {
273
            $values = $entry->{$this->attachedToFakeField};
274
            $values = is_string($values) ? json_decode($values, true) : $values;
275
            $values = $values[$this->getAttributeName()] ?? null;
276
        }
277
278
        $values ??= $entry->{$this->getAttributeName()};
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $values does not seem to be defined for all execution paths leading up to this point.
Loading history...
279
280
        if ($values === null) {
281
            return;
282
        }
283
284
        if ($this->handleMultipleFiles) {
285
            // ensure we have an array of values when field is not casted in model.
286
            if (! isset($entry->getCasts()[$this->name]) && is_string($values)) {
287
                $values = json_decode($values, true);
288
            }
289
            foreach ($values ?? [] as $value) {
290
                $value = Str::start($value, $this->path);
291
                Storage::disk($this->disk)->delete($value);
292
            }
293
294
            return;
295
        }
296
297
        $values = Str::start($values, $this->path);
298
        Storage::disk($this->disk)->delete($values);
299
    }
300
301
    private function performFileDeletion(Model $entry)
302
    {
303
        if (! $this->handleRepeatableFiles) {
304
            $this->deleteFiles($entry);
305
306
            return;
307
        }
308
309
        $this->deleteRepeatableFiles($entry);
310
    }
311
312
    /*******************************
313
     * helper methods
314
     *******************************/
315
    private function getPathFromConfiguration(array $crudObject, array $configuration): string
316
    {
317
        $this->path = $configuration['path'] ?? $crudObject['prefix'] ?? $this->path;
318
319
        return empty($this->path) ? $this->path : Str::of($this->path)->finish('/')->value();
320
    }
321
322
    private function getOriginalValue(Model $entry, $field = null)
323
    {
324
        $field ??= $this->getAttributeName();
325
326
        if ($this->updatedPreviousFiles !== null) {
327
            return $this->updatedPreviousFiles;
328
        }
329
330
        $previousValue = $entry->getOriginal($field);
331
332
        if (! $previousValue) {
333
            return $previousValue;
334
        }
335
336
        if (
337
            method_exists($entry, 'translationEnabled') &&
338
            $entry->translationEnabled() &&
339
            $entry->isTranslatableAttribute($field)
340
        ) {
341
            return $previousValue[$entry->getLocale()] ?? null;
342
        }
343
344
        return $previousValue;
345
    }
346
}
347