FileUploadBehavior::getFilePath()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
namespace yiicod\fileupload\models\behaviors;
4
5
use Exception;
6
use Symfony\Component\Filesystem\Filesystem;
7
use Yii;
8
use yii\base\Behavior;
9
use yii\base\Event;
10
use yii\db\ActiveRecord;
11
use yii\helpers\FileHelper;
12
use yiicod\fileupload\events\RemoveAllFiles;
13
use yiicod\fileupload\events\RemoveFile;
14
use yiicod\fileupload\helpers\FileUpload;
15
16
/**
17
 * Coco behavior uploader
18
 *
19
 * @author Orlov Alexey <[email protected]>
20
 */
21
class FileUploadBehavior extends Behavior
22
{
23
    /**
24
     * Events
25
     */
26
    const EVENT_REMOVE_FILE = 'removeFile';
27
    const EVENT_REMOVE_ALL_FILES = 'removeAllFiles';
28
29
    /**
30
     * File max name length
31
     *
32
     * @var int
33
     */
34
    public $maxLength = 50;
35
36
    /**
37
     * An array where keys are fields that contain file
38
     */
39
    public $fields = [];
40
    /**
41
     * mkdir mode
42
     *
43
     * @var int
44
     */
45
    public $mode = 0755;
46
47
    /**
48
     * @var array TmpRepositoryInterface config
49
     */
50
    public $tmpRepositoryClass = [
51
        'class' => TmpRepository::class,
52
    ];
53
54
    /**
55
     * @var array TmpRepositoryInterface config
56
     */
57
    public $sourceRepositoryClass = [
58
        'class' => SourceRepository::class,
59
        'uploadUrl' => '',
60
        'uploadDir' => '',
61
    ];
62
63
    /**
64
     * Prepared values
65
     *
66
     * @var array
67
     */
68
    protected $fieldsValues = [];
69
70
    /**
71
     * Origin values
72
     *
73
     * @var array
74
     */
75
    protected $originValues = [];
76
77
    /**
78
     * @var TmpRepositoryInterface
79
     */
80
    private $tmpRepository;
81
82
    /**
83
     * @var SourceRepositoryInterface
84
     */
85
    private $sourceRepository;
86
87
    /**
88
     * Set tmp file
89
     *
90
     * @param $fileName
91
     * @param $field
92
     *
93
     * @return bool
94
     */
95
    public function setTmpFile($filePath, $field): bool
96
    {
97
        return $this->getTmpRepository()->setFile($filePath, $field);
98
    }
99
100
    /**
101
     * Get tmp file
102
     *
103
     * @param $field
104
     *
105
     * @return string
106
     */
107
    public function getTmpFile($field): string
108
    {
109
        return $this->getTmpRepository()->getFile($field);
110
    }
111
112
    /**
113
     * Remove file by session key
114
     *
115
     * @param string $field
116
     * @param bool $fieldReset
117
     */
118
    public function removeTmpFile(string $field, bool $fieldReset = false)
119
    {
120
        $this->getTmpRepository()->removeFile($field);
121
        if (true === $fieldReset) {
122
            $this->owner->{$field} = '';
123
        }
124
    }
125
126
    /**
127
     * Remove field value and relative physical file
128
     *
129
     * @param string $field
130
     */
131
    public function removeFile(string $field)
132
    {
133
        Event::trigger($this, self::EVENT_REMOVE_FILE, new RemoveFile($field, $this->getSourceRepository()->getUploadDir(), $this->getFilePath($field)));
134
135
        $this->owner->{$field} = '';
136
        $this->owner->save(false);
137
138
        $fs = new Filesystem();
139
        $fs->remove($this->getFilePath($field));
140
    }
141
142
    /**
143
     * Remove all files relative to model
144
     */
145
    public function removeFiles()
146
    {
147
        Event::trigger($this, self::EVENT_REMOVE_ALL_FILES, new RemoveAllFiles($this->getSourceRepository()->getUploadDir(), $this->getFolderPath()));
148
149
        $fs = new Filesystem();
150
        $fs->remove($this->getFolderPath());
151
    }
152
153
    /**
154
     * @return TmpRepositoryInterface
155
     */
156
    public function getTmpRepository(): TmpRepositoryInterface
157
    {
158
        if (null === $this->tmpRepository) {
159
            $this->tmpRepository = Yii::createObject($this->tmpRepositoryClass, [$this->owner]);
160
        }
161
162
        return $this->tmpRepository;
163
    }
164
165
    /**
166
     * @return SourceRepositoryInterface
167
     */
168
    public function getSourceRepository(): SourceRepositoryInterface
169
    {
170
        if (null === $this->sourceRepository) {
171
            $this->sourceRepository = Yii::createObject(array_merge($this->sourceRepositoryClass, [
172
                'owner' => $this->owner,
173
            ]));
174
        }
175
176
        return $this->sourceRepository;
177
    }
178
179
    /**
180
     * Get folder path
181
     *
182
     * @param string $field Field name
183
     *
184
     * @return string Return path to entity img with|out field name
185
     */
186
    public function getFolderPath(string $field = ''): string
187
    {
188
        return $this->getSourceRepository()->getFolderPath($field);
189
    }
190
191
    /**
192
     * Get file path by "getFolderPath"
193
     *
194
     * @param string $field Field name
195
     *
196
     * @return string Return path to file
197
     */
198
    public function getFilePath(string $field): string
199
    {
200
        return $this->getSourceRepository()->getFilePath($field);
201
    }
202
203
    /**
204
     * Get file src
205
     *
206
     * @param string $field
207
     * @param null $default
208
     * @param array $params
209
     *
210
     * @return string File src
211
     */
212
    public function getFileSrc(string $field, $default = '', array $params = []): string
213
    {
214
        $repository = $this->getSourceRepository();
215
        $result = $repository->getFileSrc($field, $default, $params);
0 ignored issues
show
Bug introduced by
It seems like $default defined by parameter $default on line 212 can also be of type string; however, yiicod\fileupload\models...Interface::getFileSrc() does only seem to accept null, 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...
Unused Code introduced by
The call to SourceRepositoryInterface::getFileSrc() has too many arguments starting with $params.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
216
217
        return $result;
218
    }
219
220
    /**
221
     * Clean data if was exception or return old data if record is not new
222
     *
223
     * @param $field
224
     */
225
    protected function cleanOnException($field)
226
    {
227
        $this->removeTmpFile($field);
228
        if (false === $this->owner->isNewRecord) {
229
            $this->owner->attributes = $this->originValues;
230
            $this->owner->update();
231
        }
232
    }
233
234
    /**
235
     * Prepare field for model
236
     */
237
    protected function prepareFields()
238
    {
239
        foreach ($this->fields as $field) {
240
            if (false === isset($this->fieldsValues[$field])) {
241
                $file = $this->getTmpRepository()->getFile($field);
242
                if ($file) {
243
                    $ext = pathinfo($file, PATHINFO_EXTENSION);
244
                    $basename = basename($file);
245
                    $maxLength = min(mb_strlen($basename) - mb_strlen($ext) - 1, $this->maxLength - mb_strlen($ext) - 1);
246
                    $filename = sprintf('%s.%s', mb_substr(basename($file), 0, $maxLength), $ext);
247
                    $this->owner->{$field} = $filename;
248
                } elseif (false === file_exists($this->getFilePath($field))) {
249
                    //@todo Think about this. Because can not be record without files
250
                    Yii::info('File does not exist: ' . $this->getFilePath($field), 'fileupload');
251
                }
252
            }
253
            $this->fieldsValues[$field] = $this->owner->{$field};
254
        }
255
    }
256
257
    /**
258
     * Events
259
     *
260
     * @return array
261
     */
262
    public function events(): array
263
    {
264
        return [
265
            ActiveRecord::EVENT_AFTER_FIND => 'afterFind',
266
            ActiveRecord::EVENT_BEFORE_VALIDATE => 'beforeValidate',
267
            ActiveRecord::EVENT_BEFORE_INSERT => 'beforeSave',
268
            ActiveRecord::EVENT_BEFORE_UPDATE => 'beforeSave',
269
            ActiveRecord::EVENT_AFTER_INSERT => 'afterSave',
270
            ActiveRecord::EVENT_AFTER_UPDATE => 'afterSave',
271
            ActiveRecord::EVENT_AFTER_DELETE => 'afterDelete',
272
        ];
273
    }
274
275
    /**
276
     * Save origin attributes to temp data
277
     *
278
     * @param Event $event event parameter
279
     */
280
    public function afterFind($event)
0 ignored issues
show
Unused Code introduced by
The parameter $event is not used and could be removed.

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

Loading history...
281
    {
282
        foreach ($this->fields as $field) {
283
            if ($this->owner->hasAttribute($field)) {
284
                $this->originValues[$field] = $this->owner->{$field};
285
            }
286
        }
287
    }
288
289
    /**
290
     * Prepare fields before validate
291
     *
292
     * @param Event $event parameter
293
     *
294
     * @author Orlov Alexey <[email protected]>
295
     */
296
    public function beforeValidate($event)
0 ignored issues
show
Unused Code introduced by
The parameter $event is not used and could be removed.

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

Loading history...
297
    {
298
        $this->prepareFields();
299
    }
300
301
    /**
302
     * If model save with flag false, call method for prepare field
303
     *
304
     * @param Event $event parameter
305
     */
306
    public function beforeSave($event)
0 ignored issues
show
Unused Code introduced by
The parameter $event is not used and could be removed.

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

Loading history...
307
    {
308
        $this->prepareFields();
309
    }
310
311
    /**
312
     * After save, file move in folder for model and delete temp file
313
     *
314
     * @param Event $event event parameter
315
     *
316
     * @throws Exception
317
     */
318
    public function afterSave($event)
0 ignored issues
show
Unused Code introduced by
The parameter $event is not used and could be removed.

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

Loading history...
319
    {
320
        foreach ($this->fields as $field) {
321
            if ($tmpPath = $this->getTmpFile($field)) {
322
                if (false === is_dir($this->getFolderPath($field))) {
323
                    if (false === FileHelper::createDirectory($this->getFolderPath($field), $this->mode, true)) {
324
                        $this->cleanOnException($field);
325
                        throw new FileException('Can\'t create directory!', 'Can\'t create directory: ' . $this->getFilePath($field), 500);
326
                    }
327
                }
328
                $this->owner->{$field} = $this->fieldsValues[$field];
329
                if (!@copy($tmpPath, $this->getFilePath($field))) {
330
                    $this->cleanOnException($field);
331
                    throw new FileException('File can\'t copy from!', 'File can\'t copy from ' . $tmpPath . ' to dest: ' . $this->getFilePath($field), 500);
332
                }
333
                $this->removeTmpFile($field);
334
            }
335
            $this->removeTmpFile($field);
336
        }
337
    }
338
339
    /**
340
     * Delete files from the server before removing data from the database.
341
     *
342
     * @param Event $event event parameter
343
     */
344
    public function afterDelete($event)
0 ignored issues
show
Unused Code introduced by
The parameter $event is not used and could be removed.

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

Loading history...
345
    {
346
        $this->removeFiles();
347
    }
348
}
349