Completed
Push — master ( 7b3b63...9c00c4 )
by Song
03:37
created

UploadField::options()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 6
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Encore\Admin\Form\Field;
4
5
use Encore\Admin\Form;
6
use Illuminate\Support\Facades\Storage;
7
use Illuminate\Support\Facades\URL;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Encore\Admin\Form\Field\URL.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
8
use Symfony\Component\HttpFoundation\File\UploadedFile;
9
10
trait UploadField
11
{
12
    /**
13
     * Upload directory.
14
     *
15
     * @var string
16
     */
17
    protected $directory = '';
18
19
    /**
20
     * File name.
21
     *
22
     * @var null
23
     */
24
    protected $name = null;
25
26
    /**
27
     * Storage instance.
28
     *
29
     * @var \Illuminate\Filesystem\Filesystem
30
     */
31
    protected $storage = '';
32
33
    /**
34
     * If use unique name to store upload file.
35
     *
36
     * @var bool
37
     */
38
    protected $useUniqueName = false;
39
40
    /**
41
     * If use sequence name to store upload file.
42
     *
43
     * @var bool
44
     */
45
    protected $useSequenceName = false;
46
47
    /**
48
     * @var bool
49
     */
50
    protected $removable = false;
51
52
    /**
53
     * Controls the storage permission. Could be 'private' or 'public'
54
     *
55
     * @var string
56
     */
57
    protected $storage_permission;
58
59
    /**
60
     * Initialize the storage instance.
61
     *
62
     * @return void.
0 ignored issues
show
Documentation introduced by
The doc-type void. could not be parsed: Unknown type name "void." at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
63
     */
64
    protected function initStorage()
65
    {
66
        $this->disk(config('admin.upload.disk'));
67
    }
68
69
    /**
70
     * Set default options form image field.
71
     *
72
     * @return void
73
     */
74
    protected function setupDefaultOptions()
75
    {
76
        $defaultOptions = [
77
            'overwriteInitial'     => false,
78
            'initialPreviewAsData' => true,
79
            'browseLabel'          => trans('admin.browse'),
80
            'showRemove'           => false,
81
            'showUpload'           => false,
82
//            'initialCaption'       => $this->initialCaption($this->value),
83
            'deleteExtraData'      => [
84
                $this->formatName($this->column) => static::FILE_DELETE_FLAG,
0 ignored issues
show
Bug introduced by
The property column does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
Bug introduced by
It seems like formatName() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
85
                static::FILE_DELETE_FLAG         => '',
86
                '_token'                         => csrf_token(),
87
                '_method'                        => 'PUT',
88
            ],
89
        ];
90
91
        if ($this->form instanceof Form) {
92
            $defaultOptions['deleteUrl'] = $this->form->resource().'/'.$this->form->model()->getKey();
0 ignored issues
show
Bug introduced by
The property form does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
93
        }
94
95
        $this->options($defaultOptions);
96
    }
97
98
    /**
99
     * Set preview options form image field.
100
     *
101
     * @return void
102
     */
103
    protected function setupPreviewOptions()
104
    {
105
        if (!$this->removable) {
106
            return;
107
        }
108
109
        $this->options([
110
            //'initialPreview'        => $this->preview(),
111
            'initialPreviewConfig' => $this->initialPreviewConfig(),
0 ignored issues
show
Bug introduced by
It seems like initialPreviewConfig() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
112
        ]);
113
    }
114
115
    /**
116
     * Allow use to remove file.
117
     *
118
     * @return $this
119
     */
120
    public function removable()
121
    {
122
        $this->removable = true;
123
124
        return $this;
125
    }
126
127
    /**
128
     * Set options for file-upload plugin.
129
     *
130
     * @param array $options
131
     *
132
     * @return $this
133
     */
134
    public function options($options = [])
135
    {
136
        $this->options = array_merge($options, $this->options);
0 ignored issues
show
Bug introduced by
The property options does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
137
138
        return $this;
139
    }
140
141
    /**
142
     * Set disk for storage.
143
     *
144
     * @param string $disk Disks defined in `config/filesystems.php`.
145
     *
146
     * @throws \Exception
147
     *
148
     * @return $this
149
     */
150
    public function disk($disk)
151
    {
152
        try {
153
            $this->storage = Storage::disk($disk);
0 ignored issues
show
Documentation Bug introduced by
It seems like \Illuminate\Support\Facades\Storage::disk($disk) of type object<Illuminate\Contra...\Filesystem\Filesystem> is incompatible with the declared type object<Illuminate\Filesystem\Filesystem> of property $storage.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
154
        } catch (\Exception $exception) {
155
            if (!array_key_exists($disk, config('filesystems.disks'))) {
156
                admin_error(
157
                    'Config error.',
158
                    "Disk [$disk] not configured, please add a disk config in `config/filesystems.php`."
159
                );
160
161
                return $this;
162
            }
163
164
            throw $exception;
165
        }
166
167
        return $this;
168
    }
169
170
    /**
171
     * Specify the directory and name for upload file.
172
     *
173
     * @param string      $directory
174
     * @param null|string $name
175
     *
176
     * @return $this
177
     */
178
    public function move($directory, $name = null)
179
    {
180
        $this->dir($directory);
181
182
        $this->name($name);
0 ignored issues
show
Bug introduced by
It seems like $name defined by parameter $name on line 178 can also be of type null; however, Encore\Admin\Form\Field\UploadField::name() does only seem to accept callable, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
183
184
        return $this;
185
    }
186
187
    /**
188
     * Specify the directory upload file.
189
     *
190
     * @param string $dir
191
     *
192
     * @return $this
193
     */
194
    public function dir($dir)
195
    {
196
        if ($dir) {
197
            $this->directory = $dir;
198
        }
199
200
        return $this;
201
    }
202
203
    /**
204
     * Set name of store name.
205
     *
206
     * @param string|callable $name
207
     *
208
     * @return $this
209
     */
210
    public function name($name)
211
    {
212
        if ($name) {
213
            $this->name = $name;
214
        }
215
216
        return $this;
217
    }
218
219
    /**
220
     * Use unique name for store upload file.
221
     *
222
     * @return $this
223
     */
224
    public function uniqueName()
225
    {
226
        $this->useUniqueName = true;
227
228
        return $this;
229
    }
230
231
    /**
232
     * Use sequence name for store upload file.
233
     *
234
     * @return $this
235
     */
236
    public function sequenceName()
237
    {
238
        $this->useSequenceName = true;
239
240
        return $this;
241
    }
242
243
    /**
244
     * Get store name of upload file.
245
     *
246
     * @param UploadedFile $file
247
     *
248
     * @return string
249
     */
250
    protected function getStoreName(UploadedFile $file)
251
    {
252
        if ($this->useUniqueName) {
253
            return $this->generateUniqueName($file);
254
        }
255
256
        if ($this->useSequenceName) {
257
            return $this->generateSequenceName($file);
258
        }
259
260
        if ($this->name instanceof \Closure) {
261
            return $this->name->call($this, $file);
262
        }
263
264
        if (is_string($this->name)) {
265
            return $this->name;
266
        }
267
268
        return $file->getClientOriginalName();
269
    }
270
271
    /**
272
     * Get directory for store file.
273
     *
274
     * @return mixed|string
275
     */
276
    public function getDirectory()
277
    {
278
        if ($this->directory instanceof \Closure) {
279
            return call_user_func($this->directory, $this->form);
280
        }
281
282
        return $this->directory ?: $this->defaultDirectory();
0 ignored issues
show
Bug introduced by
It seems like defaultDirectory() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
283
    }
284
285
    /**
286
     * Upload file and delete original file.
287
     *
288
     * @param UploadedFile $file
289
     *
290
     * @return mixed
291
     */
292
    protected function upload(UploadedFile $file)
293
    {
294
        $this->renameIfExists($file);
295
296
        if (!is_null($this->storage_permission)) {
297
            return $this->storage->putFileAs($this->getDirectory(), $file, $this->name, $this->storage_permission);
298
        }
299
300
        return $this->storage->putFileAs($this->getDirectory(), $file, $this->name);
301
    }
302
303
    /**
304
     * If name already exists, rename it.
305
     *
306
     * @param $file
307
     *
308
     * @return void
309
     */
310
    public function renameIfExists(UploadedFile $file)
311
    {
312
        if ($this->storage->exists("{$this->getDirectory()}/$this->name")) {
313
            $this->name = $this->generateUniqueName($file);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->generateUniqueName($file) of type string is incompatible with the declared type null of property $name.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
314
        }
315
    }
316
317
    /**
318
     * Get file visit url.
319
     *
320
     * @param $path
321
     *
322
     * @return string
323
     */
324
    public function objectUrl($path)
325
    {
326
        if (URL::isValidUrl($path)) {
327
            return $path;
328
        }
329
330
        if ($this->storage) {
331
            return $this->storage->url($path);
332
        }
333
334
        return Storage::disk(config('admin.upload.disk'))->url($path);
335
    }
336
337
    /**
338
     * Generate a unique name for uploaded file.
339
     *
340
     * @param UploadedFile $file
341
     *
342
     * @return string
343
     */
344
    protected function generateUniqueName(UploadedFile $file)
345
    {
346
        return md5(uniqid()).'.'.$file->getClientOriginalExtension();
347
    }
348
349
    /**
350
     * Generate a sequence name for uploaded file.
351
     *
352
     * @param UploadedFile $file
353
     *
354
     * @return string
355
     */
356
    protected function generateSequenceName(UploadedFile $file)
357
    {
358
        $index = 1;
359
        $extension = $file->getClientOriginalExtension();
360
        $originalName = $file->getClientOriginalName();
361
        $newName = $originalName.'_'.$index.'.'.$extension;
362
363
        while ($this->storage->exists("{$this->getDirectory()}/$newName")) {
364
            $index++;
365
            $newName = $originalName.'_'.$index.'.'.$extension;
366
        }
367
368
        return $newName;
369
    }
370
371
    /**
372
     * Destroy original files.
373
     *
374
     * @return void.
0 ignored issues
show
Documentation introduced by
The doc-type void. could not be parsed: Unknown type name "void." at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
375
     */
376
    public function destroy()
377
    {
378
        if ($this->storage->exists($this->original)) {
379
            $this->storage->delete($this->original);
0 ignored issues
show
Bug introduced by
The property original does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
380
        }
381
    }
382
383
    public function storage_permission($permission)
384
    {
385
        $this->storage_permission = $permission;
386
        return $this;
387
    }
388
}
389