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::afterDelete()   A
last analyzed

Complexity

Conditions 3
Paths 2

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

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