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 (#5427)
by Cristian
29:45 queued 14:40
created

Uploader::getAttributeName()   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 0
dl 0
loc 3
rs 10
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\Collection;
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: $pivot, $exists, $pivotParent
Loading history...
18
19
    private string $name;
20
21
    private string $disk = 'public';
22
23
    private string $path = '';
24
25
    private 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
        if ($this->attachedToFakeField) {
81
            $fakeFieldValue = $entry->{$this->attachedToFakeField};
82
            $fakeFieldValue = is_string($fakeFieldValue) ? json_decode($fakeFieldValue, true) : (array) $fakeFieldValue;
83
            $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...
84
85
            $entry->{$this->attachedToFakeField} = isset($entry->getCasts()[$this->attachedToFakeField]) ? $fakeFieldValue : json_encode($fakeFieldValue);
86
87
            return $entry;
88
        }
89
90
        $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...
91
92
        return $entry;
93
    }
94
95
    public function retrieveUploadedFiles(Model $entry): Model
96
    {
97
        if ($this->handleRepeatableFiles) {
98
            return $this->retrieveRepeatableFiles($entry);
99
        }
100
101
        return $this->retrieveFiles($entry);
102
    }
103
104
    public function deleteUploadedFiles(Model $entry): void
105
    {
106
        if ($this->deleteWhenEntryIsDeleted) {
107
            if (! in_array(SoftDeletes::class, class_uses_recursive($entry), true)) {
108
                $this->performFileDeletion($entry);
109
110
                return;
111
            }
112
113
            if ($entry->isForceDeleting() === true) {
114
                $this->performFileDeletion($entry);
115
            }
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 getIdentifier(): string
158
    {
159
        if ($this->handleRepeatableFiles) {
160
            return $this->repeatableContainerName.'_'.$this->name;
161
        }
162
163
        return $this->name;
164
    }
165
166
    public function getNameForRequest(): string
167
    {
168
        return $this->repeatableContainerName ?? $this->name;
169
    }
170
171
    public function canHandleMultipleFiles(): bool
172
    {
173
        return $this->handleMultipleFiles;
174
    }
175
176
    public function isRelationship(): bool
177
    {
178
        return $this->isRelationship;
179
    }
180
181
    public function getPreviousFiles(Model $entry): mixed
182
    {
183
        if (! $this->attachedToFakeField) {
184
            return $this->getOriginalValue($entry);
185
        }
186
187
        $value = $this->getOriginalValue($entry, $this->attachedToFakeField);
188
        $value = is_string($value) ? json_decode($value, true) : (array) $value;
189
190
        return $value[$this->getAttributeName()] ?? null;
191
    }
192
193
    /*******************************
194
     * Setters - fluently configure the uploader
195
     *******************************/
196
    public function multiple(): self
197
    {
198
        $this->handleMultipleFiles = true;
199
200
        return $this;
201
    }
202
203
    public function relationship(bool $isRelationship): self
204
    {
205
        $this->isRelationship = $isRelationship;
206
207
        return $this;
208
    }
209
210
    /*******************************
211
     * Default implementation functions
212
     *******************************/
213
    public function uploadFiles(Model $entry, $values = null)
0 ignored issues
show
Unused Code introduced by
The parameter $values is not used and could be removed. ( Ignorable by Annotation )

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

213
    public function uploadFiles(Model $entry, /** @scrutinizer ignore-unused */ $values = null)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
214
    {
215
    }
216
217
    private function retrieveFiles(Model $entry): Model
218
    {
219
        $value = $entry->{$this->getAttributeName()};
220
221
        if ($this->handleMultipleFiles) {
222
            if (! isset($entry->getCasts()[$this->getName()]) && is_string($value)) {
223
                $entry->{$this->getAttributeName()} = json_decode($value, true);
224
            }
225
226
            return $entry;
227
        }
228
229
        if ($this->attachedToFakeField) {
230
            $values = $entry->{$this->attachedToFakeField};
231
            $values = is_string($values) ? json_decode($values, true) : (array) $values;
232
233
            $values[$this->getAttributeName()] = isset($values[$this->getAttributeName()]) ? Str::after($values[$this->getAttributeName()], $this->path) : null;
234
            $entry->{$this->attachedToFakeField} = json_encode($values);
235
236
            return $entry;
237
        }
238
239
        $entry->{$this->getAttributeName()} = Str::after($value, $this->path);
240
241
        return $entry;
242
    }
243
244
    private function deleteFiles(Model $entry)
245
    {
246
        $values = $entry->{$this->getAttributeName()};
247
248
        if ($values === null) {
249
            return;
250
        }
251
252
        if ($this->handleMultipleFiles) {
253
            // ensure we have an array of values when field is not casted in model.
254
            if (! isset($entry->getCasts()[$this->name]) && is_string($values)) {
255
                $values = json_decode($values, true);
256
            }
257
            foreach ($values ?? [] as $value) {
258
                $value = Str::start($value, $this->path);
259
                Storage::disk($this->disk)->delete($value);
260
            }
261
262
            return;
263
        }
264
265
        $values = Str::start($values, $this->path);
266
        Storage::disk($this->disk)->delete($values);
267
    }
268
269
    private function performFileDeletion(Model $entry)
270
    {
271
        if (! $this->handleRepeatableFiles) {
272
            $this->deleteFiles($entry);
273
274
            return;
275
        }
276
277
        $this->deleteRepeatableFiles($entry);
278
    }
279
280
    /*******************************
281
     * Private helper methods
282
     *******************************/
283
    private function getPathFromConfiguration(array $crudObject, array $configuration): string
284
    {
285
        $this->path = $configuration['path'] ?? $crudObject['prefix'] ?? $this->path;
286
287
        return empty($this->path) ? $this->path : Str::of($this->path)->finish('/')->value();
288
    }
289
290
    private function getOriginalValue(Model $entry, $field = null)
291
    {
292
        if ($this->updatedPreviousFiles !== null) {
293
            return $this->updatedPreviousFiles;
294
        }
295
296
        $previousValue = $entry->getOriginal($field ?? $this->getAttributeName());
297
298
        if (! $previousValue) {
299
            return $previousValue;
300
        }
301
302
        if (method_exists($entry, 'translationEnabled') && $entry->translationEnabled()) {
303
            return $previousValue[$entry->getLocale()] ?? null;
304
        }
305
306
        return $previousValue;
307
    }
308
309
    /*******************************
310
     * Helper methods
311
     *******************************/
312
313
    /**
314
     * Given two multidimensional arrays/collections, merge them recursively.
315
     */
316
    public static function mergeFilesAndValuesRecursive(array|Collection $array1, array|Collection $array2): array|Collection
317
    {
318
        $merged = $array1;
319
        foreach ($array2 as $key => &$value) {
320
            if (is_array($value) && isset($merged[$key]) && is_array($merged[$key])) {
321
                $merged[$key] = self::mergeFilesAndValuesRecursive($merged[$key], $value);
322
            } else {
323
                $merged[$key] = $value;
324
            }
325
        }
326
327
        return $merged;
328
    }
329
}
330