Completed
Push — master ( 2bd67e...423e5d )
by Taosikai
37:51 queued 22:49
created

Uploader.php (3 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * slince upload handler library
4
 * @author Tao <[email protected]>
5
 */
6
namespace Slince\Upload;
7
8
use Slince\Upload\Exception\UploadException;
9
use Slince\Upload\Rule\RuleInterface;
10
use Slince\Upload\Rule\SystemRule;
11
12
class Uploader
13
{
14
    /**
15
     * Whether to overwrite if there is a file of the same name
16
     * @var boolean
17
     */
18
    protected $override = false;
19
20
    /**
21
     * save path
22
     * @var string
23
     */
24
    protected $savePath = './';
25
26
    /**
27
     * Whether to enable random file name
28
     * @var boolean
29
     */
30
    protected $isRandName = false;
31
32
    /**
33
     * rules
34
     * @var RuleInterface[]
35
     */
36
    protected $rules = [];
37
38
    /**
39
     * file name generator
40
     * @var callable
41
     */
42
    protected $filenameGenerator;
43
44
    public function __construct($path = './')
45
    {
46
        $this->setSavePath($path);
47
        $this->addRule(new SystemRule());
48
    }
49
50
    /**
51
     * set override mode
52
     * @param boolean $override
53
     */
54
    public function setOverride($override)
55
    {
56
        $this->override = $override;
57
    }
58
59
    /**
60
     * get override mode
61
     * @return boolean
62
     */
63
    public function getOverride()
64
    {
65
        return $this->override;
66
    }
67
68
    /**
69
     * set save path
70
     * @param string $path
71
     * @throws UploadException
72
     */
73
    public function setSavePath($path)
74
    {
75
        if (!file_exists($path)) {
76
            @mkdir($path, 0777, true);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
77
        }
78
        if (!is_dir($path)) {
79
            throw new UploadException(sprintf('Path "%s" is not valid', $path));
80
        }
81
        $this->savePath = rtrim($path, '\\/') . DIRECTORY_SEPARATOR;
82
    }
83
84
    /**
85
     * get save path
86
     * @return string
87
     */
88
    public function getSavePath()
89
    {
90
        return $this->savePath;
91
    }
92
93
    /**
94
     * set rand name mode
95
     * @param boolean $val
96
     */
97
    public function setIsRandName($val)
98
    {
99
        $this->isRandName = $val;
100
    }
101
102
    /**
103
     * get rand name mode
104
     * @return boolean
105
     */
106
    public function getIsRandName()
107
    {
108
        return $this->isRandName;
109
    }
110
111
    /**
112
     * set filename generator
113
     * @param callable $generator
114
     */
115
    public function setFilenameGenerator(callable $generator)
116
    {
117
        $this->filenameGenerator = $generator;
118
    }
119
120
    /**
121
     * get current filename generator
122
     * @return string
123
     */
124
    public function getFilenameGenerator()
125
    {
126
        if (is_null($this->filenameGenerator)) {
127
            $this->filenameGenerator = $this->isRandName ? $this->makeDefaultRandFilenameGenerator()
128
                : $this->makeDefaultOriginFilenameGenerator();
129
        }
130
        return $this->filenameGenerator;
131
    }
132
133
    /**
134
     * add rule
135
     * @param RuleInterface $rule
136
     */
137
    public function addRule(RuleInterface $rule)
138
    {
139
        $this->rules[] = $rule;
140
    }
141
142
    /**
143
     * gets all rules
144
     * @return array
145
     */
146
    public function getRules()
147
    {
148
        return (array)$this->rules;
149
    }
150
151
    /**
152
     * go, process upload
153
     * @param array $files
154
     * @throws UploadException
155
     * @return FileInfo|FileInfo[];
0 ignored issues
show
The doc-type FileInfo|FileInfo[]; could not be parsed: Expected "|" or "end of type", but got ";" at position 19. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
156
     */
157
    public function process($files)
158
    {
159
        if (empty($files)) {
160
            throw new UploadException('File array is not valid');
161
        }
162
        //multi files
163
        if (is_array($files['name'])) {
164
            $_files = [];
165
            foreach ($files['name'] as $key => $fileName) {
166
                $file = array(
167
                    'error' => $files['error'][$key],
168
                    'name' => $fileName,
169
                    'size' => $files['size'][$key],
170
                    'tmp_name' => $files['tmp_name'][$key],
171
                    'type' => $files['type'][$key]
172
                );
173
                $_files[] = $this->processUpload($file);
174
            }
175
            return $_files;
176
        } else {
177
            return $this->processUpload($files);
178
        }
179
    }
180
181
    /**
182
     * process
183
     * @param array $info
184
     * @return FileInfo;
0 ignored issues
show
The doc-type FileInfo; could not be parsed: Expected "|" or "end of type", but got ";" at position 8. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
185
     */
186
    protected function processUpload(array $info)
187
    {
188
        $file = FileInfo::fromArray($info);
189
        if ($this->validateUpload($file)) {
190
            $newFilePath = $this->generateFilename($file);
191
            $result = $this->moveUploadedFile($file, $newFilePath);
192
            if ($result) {
193
                $file->setPath($newFilePath);
194
                $file->setHasError(false);
195
            } else {
196
                $file->setHasError(true);
197
            }
198
        }
199
        return $file;
200
    }
201
202
    /**
203
     * validate rules
204
     * @param FileInfo $file
205
     * @return boolean
206
     */
207
    protected function validateUpload(FileInfo $file)
208
    {
209
        foreach ($this->rules as $rule) {
210
            if (!$rule->validate($file)) {
211
                $file->setErrorCode($rule->getErrorCode());
212
                $file->setErrorMsg($rule->getErrorMsg());
213
                return false;
214
            }
215
        }
216
        return true;
217
    }
218
219
    /**
220
     * move file
221
     * Illegal upload files and other unsolicited causes can not be moved to throw an exception
222
     * @param FileInfo $file
223
     * @param string $newFilePath
224
     * @return boolean
225
     * @throws UploadException
226
     */
227
    protected function moveUploadedFile(FileInfo $file, $newFilePath)
228
    {
229
        $tmpName = $file->getTmpName();
230
        if (is_uploaded_file($tmpName)) {
231
            if (!file_exists($newFilePath) || $this->override) {
232
                if (!@move_uploaded_file($tmpName, $newFilePath)) {
233
                    throw new UploadException('Failed to move file');
234
                }
235
                return true;
236
            } else {
237
                $file->setErrorCode(ErrorStore::ERROR_SAME_NAME_FILE);
238
                $file->setErrorMsg(sprintf('File "%s" already exists', $file->getOriginName()));
239
                return false;
240
            }
241
        }
242
        throw new UploadException('The uploaded file is invalid');
243
    }
244
245
    /**
246
     * make default rand filename generator
247
     * @return callable
248
     */
249
    protected function makeDefaultRandFilenameGenerator()
250
    {
251
        return function (FileInfo $file) {
252
            return $this->savePath . time() . rand(10, 99) . '.' . $file->getExtension();
253
        };
254
    }
255
256
    /**
257
     * make default origin filename generator
258
     * @return callable
259
     */
260
    protected function makeDefaultOriginFilenameGenerator()
261
    {
262
        return function (FileInfo $file) {
263
            return $this->savePath . $file->getOriginName();
264
        };
265
    }
266
267
    /**
268
     * make new filepath
269
     * @param FileInfo $file
270
     * @return string
271
     */
272
    protected function generateFilename(FileInfo $file)
273
    {
274
        $generator = $this->getFilenameGenerator();
275
        $path = call_user_func($generator, $file);
276
        return $path;
277
    }
278
}
279