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

Passed
Push — clean-up-uploaders ( 67430a )
by Pedro
13:03
created

Uploader::shouldUploadFiles()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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