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 (#51)
by
unknown
03:41
created

UploadBehavior::getTimestamp()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 8
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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