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.
Completed
Pull Request — master (#50)
by
unknown
03:39
created

UploadBehavior::delete()   B

Complexity

Conditions 5
Paths 6

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

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