Passed
Push — master ( c38375...7723a2 )
by Aleksandr
01:27
created

Uploader::context()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace carono\yii2file;
4
5
use Yii;
6
use yii\base\Component;
7
use yii\base\Security;
8
use yii\db\ActiveRecord;
9
use yii\db\Expression;
10
use yii\helpers\ArrayHelper;
11
use yii\helpers\FileHelper;
12
use yii\web\UploadedFile;
13
14
/**
15
 * Class FileUpload
16
 *
17
 * @package carono\components
18
 * @property string $fullName
19
 */
20
class Uploader extends Component
21
{
22
    /**
23
     * @var FileUploadTrait
24
     */
25
    public $modelClass;
26
    public $data;
27
    public $file;
28
    public $name;
29
    public $slug;
30
    public $uid;
31
    public $delete = true;
32
    public $folder;
33
    public $context;
34
    protected $fileName;
35
    protected $filePath;
36
37
    /**
38
     * @param $name
39
     * @return $this
40
     */
41
    public function name($name)
42
    {
43
        $this->name = $name;
44
        return $this;
45
    }
46
47
    /**
48
     * @param $data
49
     * @return $this
50
     */
51
    public function data($data)
52
    {
53
        $this->data = $data;
54
        return $this;
55
    }
56
57
    /**
58
     * @param $path
59
     * @return $this
60
     */
61
    public function folder($path)
62
    {
63
        $this->folder = $path;
64
        return $this;
65
    }
66
67
    public function context($context)
68
    {
69
        $this->context = $context;
70
        return $this;
71
    }
72
73
    /**
74
     * @param bool $deleteOnFinish
75
     * @return $this
76
     */
77
    public function delete($deleteOnFinish = true)
78
    {
79
        $this->delete = $deleteOnFinish;
80
        return $this;
81
    }
82
83
    /**
84
     * @return null|string
85
     */
86
    protected function getSession()
87
    {
88
        if (isset(Yii::$app->session)) {
89
            $session = Yii::$app->session->getIsActive() ? Yii::$app->session->getId() : null;
0 ignored issues
show
Bug introduced by
The method getIsActive() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

89
            $session = Yii::$app->session->/** @scrutinizer ignore-call */ getIsActive() ? Yii::$app->session->getId() : null;

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
90
        } else {
91
            $session = null;
92
        }
93
        return $session;
94
    }
95
96
    /**
97
     * @return mixed
98
     */
99
    public function getFileName()
100
    {
101
        return $this->name ?: $this->fileName;
102
    }
103
104
    /**
105
     * @return string
106
     */
107
    protected function getFileNameWithOutExtension()
108
    {
109
        return basename($this->getFileName(), '.' . $this->getFileExtension());
110
    }
111
112
    /**
113
     * @return string
114
     */
115
    protected function getMimeType()
116
    {
117
        return FileHelper::getMimeType($this->filePath);
118
    }
119
120
    /**
121
     * @return mixed|string
122
     */
123
    protected function getFileExtension()
124
    {
125
        if (!$extension = strtolower(pathinfo($this->fileName, PATHINFO_EXTENSION))) {
126
            $extension = ArrayHelper::getValue(FileHelper::getExtensionsByMimeType($this->getMimeType()), 0);
127
        }
128
        return $extension;
129
    }
130
131
    /**
132
     * @return int|null|string
133
     */
134
    public function getUserId()
135
    {
136
        if (isset(Yii::$app->user)) {
137
            $userId = Yii::$app->user->getId();
0 ignored issues
show
Bug introduced by
The method getId() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

137
            /** @scrutinizer ignore-call */ 
138
            $userId = Yii::$app->user->getId();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
138
        } else {
139
            $userId = null;
140
        }
141
        return $userId;
142
    }
143
144
    /**
145
     * @param $source
146
     * @param $destination
147
     * @throws \Exception
148
     */
149
    protected function copyUploadedFile($source, $destination)
150
    {
151
        if (!move_uploaded_file($source, $destination)) {
152
            throw new \Exception('Unknown upload error');
153
        }
154
    }
155
156
    /**
157
     * @param $source
158
     * @param $destination
159
     * @throws \Exception
160
     */
161
    protected function renameFile($source, $destination)
162
    {
163
        if (!rename($source, $destination)) {
164
            throw new \Exception('Failed rename file');
165
        }
166
    }
167
168
    /**
169
     * @param $source
170
     * @param $destination
171
     * @throws \Exception
172
     */
173
    protected function copyFile($source, $destination)
174
    {
175
        if (!copy($source, $destination)) {
176
            throw new \Exception('Failed copy file');
177
        }
178
    }
179
180
    /**
181
     * @param $source
182
     * @param $destination
183
     * @throws \Exception
184
     */
185
    public function copy($source, $destination)
186
    {
187
        if (is_uploaded_file($source)) {
188
            $this->copyUploadedFile($source, $destination);
189
        } elseif ($this->delete) {
190
            $this->renameFile($source, $destination);
191
        } else {
192
            $this->copyFile($source, $destination);
193
        }
194
    }
195
196
    /**
197
     * @return string
198
     */
199
    public function getUid()
200
    {
201
        return $this->uid = $this->uid ?: $this->formUid();
202
    }
203
204
    /**
205
     * @return string
206
     */
207
    public function getNewFileAlias()
208
    {
209
        return static::formAliasPath($this->getUid(), $this->folder);
210
    }
211
212
    /**
213
     * @param ActiveRecord $model
214
     * @param $attributes
215
     */
216
    public function loadAttributes($model, $attributes)
217
    {
218
        $model->setAttributes($attributes);
219
    }
220
221
    public function formAttributes()
222
    {
223
        $this->processFilePath($this->file);
224
        return [
225
            'session' => $this->getSession(),
226
            'name' => $this->getFileNameWithOutExtension(),
227
            'extension' => $this->getFileExtension(),
228
            'user_id' => $this->getUserId(),
229
            'uid' => $this->getUid(),
230
            'data' => !is_null($this->data) ? json_encode($this->data) : null,
231
            'mime_type' => $this->getMimeType(),
232
            'md5' => md5_file($this->filePath),
233
            'folder' => static::formAliasPath($this->getUid(), $this->folder),
234
            'slug' => $this->slug,
235
            'size' => filesize($this->filePath)
236
        ];
237
    }
238
239
    /**
240
     * @return FileUploadTrait|null
241
     * @throws \Exception
242
     */
243
    public function process()
244
    {
245
        /**
246
         * @var FileUploadTrait $model
247
         */
248
        $model = new $this->modelClass();
249
        $this->loadAttributes($model, $this->formAttributes());
0 ignored issues
show
Bug introduced by
$model of type carono\yii2file\FileUploadTrait is incompatible with the type yii\db\ActiveRecord expected by parameter $model of carono\yii2file\Uploader::loadAttributes(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

249
        $this->loadAttributes(/** @scrutinizer ignore-type */ $model, $this->formAttributes());
Loading history...
250
        $newFilePath = $model->getRealFilePath();
251
        if (!is_dir($realFolder = dirname($newFilePath))) {
252
            if (!mkdir($realFolder, 0777, true) && !is_dir($realFolder)) {
253
                throw new \RuntimeException(sprintf('Directory "%s" was not created', $realFolder));
254
            }
255
        }
256
        if (!file_exists($this->filePath)) {
257
            throw new \Exception('File not loaded or not exist');
258
        }
259
        $this->copy($this->filePath, $newFilePath);
260
        if ($model->save()) {
0 ignored issues
show
Bug introduced by
It seems like save() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

260
        if ($model->/** @scrutinizer ignore-call */ save()) {
Loading history...
261
            return $model;
262
        }
263
264
        $model->deleteFile();
265
        return null;
266
    }
267
268
    /**
269
     * @param $file
270
     */
271
    protected function processFilePath($file)
272
    {
273
        if (strpos($file, 'http') === 0) {
274
            $context = $this->context ? stream_context_create($this->context) : null;
275
            $tmp = Yii::getAlias('@runtime') . DIRECTORY_SEPARATOR . uniqid('file_upload_', true);
276
            file_put_contents($tmp, file_get_contents($file, false, $context));
277
            $this->filePath = $tmp;
278
            $this->fileName = explode('?', basename($file))[0];
279
        } elseif (is_string($file)) {
280
            $this->filePath = Yii::getAlias($file);
281
            $this->fileName = basename($this->filePath);
282
        } elseif ($file instanceof UploadedFile) {
283
            $this->filePath = $file->tempName;
284
            $this->fileName = $file->name;
285
        }
286
    }
287
288
    /**
289
     * @param $slug
290
     * @return $this
291
     */
292
    public function slug($slug)
293
    {
294
        $this->slug = $slug;
295
        return $this;
296
    }
297
298
    /**
299
     * @return string
300
     */
301
    protected function generateUid()
302
    {
303
        $sec = new Security();
304
        return md5($sec->generateRandomString(64));
305
    }
306
307
    /**
308
     * @return string
309
     */
310
    protected function formUid()
311
    {
312
        $class = $this->modelClass;
313
        do {
314
            $uPath = $this->generateUid();
315
        } while ($class::find()->where(['uid' => $uPath])->exists());
316
        return $uPath;
317
    }
318
319
    /**
320
     * @param $path
321
     * @param null|string $aliasPrefix
322
     * @return string
323
     */
324
    public static function formAliasPath($path, $aliasPrefix = null)
325
    {
326
        $p = $aliasPrefix ? [$aliasPrefix] : [];
327
        for ($i = 0; $i < 3; $i++) {
328
            $p[] = $path[$i];
329
        }
330
        return implode('/', $p);
331
    }
332
}