GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

UploadBehavior::generateFileName()   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 mohorev\file;
4
5
use Closure;
6
use Yii;
7
use yii\base\Behavior;
8
use yii\base\InvalidArgumentException;
9
use yii\base\InvalidConfigException;
10
use yii\db\BaseActiveRecord;
11
use yii\helpers\ArrayHelper;
12
use yii\helpers\FileHelper;
13
use yii\web\UploadedFile;
14
15
/**
16
 * UploadBehavior automatically uploads file and fills the specified attribute
17
 * with a value of the name of the uploaded file.
18
 *
19
 * To use UploadBehavior, insert the following code to your ActiveRecord class:
20
 *
21
 * ```php
22
 * use mohorev\file\UploadBehavior;
23
 *
24
 * function behaviors()
25
 * {
26
 *     return [
27
 *         [
28
 *             'class' => UploadBehavior::class,
29
 *             'attribute' => 'file',
30
 *             'scenarios' => ['insert', 'update'],
31
 *             'path' => '@webroot/upload/{id}',
32
 *             'url' => '@web/upload/{id}',
33
 *         ],
34
 *     ];
35
 * }
36
 * ```
37
 *
38
 * @author Alexander Mohorev <[email protected]>
39
 * @author Alexey Samoylov <[email protected]>
40
 */
41
class UploadBehavior extends Behavior
42
{
43
    /**
44
     * @event Event an event that is triggered after a file is uploaded.
45
     */
46
    const EVENT_AFTER_UPLOAD = 'afterUpload';
47
48
    /**
49
     * @var string the attribute which holds the attachment.
50
     */
51
    public $attribute;
52
    /**
53
     * @var array the scenarios in which the behavior will be triggered
54
     */
55
    public $scenarios = [];
56
    /**
57
     * @var string the base path or path alias to the directory in which to save files.
58
     */
59
    public $path;
60
    /**
61
     * @var string the base URL or path alias for this file
62
     */
63
    public $url;
64
    /**
65
     * @var bool Getting file instance by name
66
     */
67
    public $instanceByName = false;
68
    /**
69
     * @var boolean|callable generate a new unique name for the file
70
     * set true or anonymous function takes the old filename and returns a new name.
71
     * @see self::generateFileName()
72
     */
73
    public $generateNewName = true;
74
    /**
75
     * @var boolean If `true` current attribute file will be deleted
76
     */
77
    public $unlinkOnSave = true;
78
    /**
79
     * @var boolean If `true` current attribute file will be deleted after model deletion.
80
     */
81
    public $unlinkOnDelete = true;
82
    /**
83
     * @var boolean $deleteTempFile whether to delete the temporary file after saving.
84
     */
85
    public $deleteTempFile = true;
86
87
    /**
88
     * @var UploadedFile the uploaded file instance.
89
     */
90
    private $_file;
91
92
93
    /**
94
     * @inheritdoc
95
     */
96
    public function init()
97
    {
98
        parent::init();
99
100
        if ($this->attribute === null) {
101
            throw new InvalidConfigException('The "attribute" property must be set.');
102
        }
103
        if ($this->path === null) {
104
            throw new InvalidConfigException('The "path" property must be set.');
105
        }
106
        if ($this->url === null) {
107
            throw new InvalidConfigException('The "url" property must be set.');
108
        }
109
    }
110
111
    /**
112
     * @inheritdoc
113
     */
114
    public function events()
115
    {
116
        return [
117
            BaseActiveRecord::EVENT_BEFORE_VALIDATE => 'beforeValidate',
118
            BaseActiveRecord::EVENT_BEFORE_INSERT => 'beforeSave',
119
            BaseActiveRecord::EVENT_BEFORE_UPDATE => 'beforeSave',
120
            BaseActiveRecord::EVENT_AFTER_INSERT => 'afterSave',
121
            BaseActiveRecord::EVENT_AFTER_UPDATE => 'afterSave',
122
            BaseActiveRecord::EVENT_AFTER_DELETE => 'afterDelete',
123
        ];
124
    }
125
126
    /**
127
     * This method is invoked before validation starts.
128
     */
129
    public function beforeValidate()
130
    {
131
        /** @var BaseActiveRecord $model */
132
        $model = $this->owner;
133
        if (in_array($model->scenario, $this->scenarios)) {
134
            if (($file = $model->getAttribute($this->attribute)) instanceof UploadedFile) {
135
                $this->_file = $file;
136
            } else {
137
                if ($this->instanceByName === true) {
138
                    $this->_file = UploadedFile::getInstanceByName($this->attribute);
139
                } else {
140
                    $this->_file = UploadedFile::getInstance($model, $this->attribute);
141
                }
142
            }
143
            if ($this->_file instanceof UploadedFile) {
144
                $this->_file->name = $this->getFileName($this->_file);
145
                $model->setAttribute($this->attribute, $this->_file);
146
            }
147
        }
148
    }
149
150
    /**
151
     * This method is called at the beginning of inserting or updating a record.
152
     */
153
    public function beforeSave()
154
    {
155
        /** @var BaseActiveRecord $model */
156
        $model = $this->owner;
157
        if (in_array($model->scenario, $this->scenarios)) {
158
            if ($this->_file instanceof UploadedFile) {
159
                if (!$model->getIsNewRecord() && $model->isAttributeChanged($this->attribute)) {
160
                    if ($this->unlinkOnSave === true) {
161
                        $this->delete($this->attribute, true);
162
                    }
163
                }
164
                $model->setAttribute($this->attribute, $this->_file->name);
165
            } else {
166
                // Protect attribute
167
                unset($model->{$this->attribute});
168
            }
169
        } else {
170
            if (!$model->getIsNewRecord() && $model->isAttributeChanged($this->attribute)) {
171
                if ($this->unlinkOnSave === true) {
172
                    $this->delete($this->attribute, true);
173
                }
174
            }
175
        }
176
    }
177
178
    /**
179
     * This method is called at the end of inserting or updating a record.
180
     * @throws \yii\base\InvalidArgumentException
181
     */
182
    public function afterSave()
183
    {
184
        if ($this->_file instanceof UploadedFile) {
185
            $path = $this->getUploadPath($this->attribute);
186
            if (is_string($path) && FileHelper::createDirectory(dirname($path))) {
187
                $this->save($this->_file, $path);
188
                $this->afterUpload();
189
            } else {
190
                throw new InvalidArgumentException(
191
                    "Directory specified in 'path' attribute doesn't exist or cannot be created."
192
                );
193
            }
194
        }
195
    }
196
197
    /**
198
     * This method is invoked after deleting a record.
199
     */
200
    public function afterDelete()
201
    {
202
        $attribute = $this->attribute;
203
        if ($this->unlinkOnDelete && $attribute) {
204
            $this->delete($attribute);
205
        }
206
    }
207
208
    /**
209
     * Returns file path for the attribute.
210
     * @param string $attribute
211
     * @param boolean $old
212
     * @return string|null the file path.
213
     */
214
    public function getUploadPath($attribute, $old = false)
215
    {
216
        /** @var BaseActiveRecord $model */
217
        $model = $this->owner;
218
        $path = $this->resolvePath($this->path);
219
        $fileName = ($old === true) ? $model->getOldAttribute($attribute) : $model->$attribute;
220
221
        return $fileName ? Yii::getAlias($path . '/' . $fileName) : null;
222
    }
223
224
    /**
225
     * Returns file url for the attribute.
226
     * @param string $attribute
227
     * @return string|null
228
     */
229
    public function getUploadUrl($attribute)
230
    {
231
        /** @var BaseActiveRecord $model */
232
        $model = $this->owner;
233
        $url = $this->resolvePath($this->url);
234
        $fileName = $model->getOldAttribute($attribute);
235
236
        return $fileName ? Yii::getAlias($url . '/' . $fileName) : null;
237
    }
238
239
    /**
240
     * Returns the UploadedFile instance.
241
     * @return UploadedFile
242
     */
243
    protected function getUploadedFile()
244
    {
245
        return $this->_file;
246
    }
247
248
    /**
249
     * Replaces all placeholders in path variable with corresponding values.
250
     */
251
    protected function resolvePath($path)
252
    {
253
        /** @var BaseActiveRecord $model */
254
        $model = $this->owner;
255
        return preg_replace_callback('/{([^}]+)}/', function ($matches) use ($model) {
256
            $name = $matches[1];
257
            $attribute = ArrayHelper::getValue($model, $name);
258
            if (is_string($attribute) || is_numeric($attribute)) {
259
                return $attribute;
260
            } else {
261
                return $matches[0];
262
            }
263
        }, $path);
264
    }
265
266
    /**
267
     * Saves the uploaded file.
268
     * @param UploadedFile $file the uploaded file instance
269
     * @param string $path the file path used to save the uploaded file
270
     * @return boolean true whether the file is saved successfully
271
     */
272
    protected function save($file, $path)
273
    {
274
        return $file->saveAs($path, $this->deleteTempFile);
275
    }
276
277
    /**
278
     * Deletes old file.
279
     * @param string $attribute
280
     * @param boolean $old
281
     */
282
    protected function delete($attribute, $old = false)
283
    {
284
        $path = $this->getUploadPath($attribute, $old);
285
        if (is_file($path)) {
286
            unlink($path);
287
        }
288
    }
289
290
    /**
291
     * @param UploadedFile $file
292
     * @return string
293
     */
294
    protected function getFileName($file)
295
    {
296
        if ($this->generateNewName) {
297
            return $this->generateNewName instanceof Closure
298
                ? call_user_func($this->generateNewName, $file)
299
                : $this->generateFileName($file);
300
        } else {
301
            return $this->sanitize($file->name);
302
        }
303
    }
304
305
    /**
306
     * Replaces characters in strings that are illegal/unsafe for filename.
307
     *
308
     * #my*  unsaf<e>&file:name?".png
309
     *
310
     * @param string $filename the source filename to be "sanitized"
311
     * @return boolean string the sanitized filename
312
     */
313
    public static function sanitize($filename)
314
    {
315
        return str_replace([' ', '"', '\'', '&', '/', '\\', '?', '#'], '-', $filename);
316
    }
317
318
    /**
319
     * Generates random filename.
320
     * @param UploadedFile $file
321
     * @return string
322
     */
323
    protected function generateFileName($file)
324
    {
325
        return uniqid() . '.' . $file->extension;
326
    }
327
328
    /**
329
     * This method is invoked after uploading a file.
330
     * The default implementation raises the [[EVENT_AFTER_UPLOAD]] event.
331
     * You may override this method to do postprocessing after the file is uploaded.
332
     * Make sure you call the parent implementation so that the event is raised properly.
333
     */
334
    protected function afterUpload()
335
    {
336
        $this->owner->trigger(self::EVENT_AFTER_UPLOAD);
337
    }
338
}
339