Completed
Push — master ( 476384...cf7d6b )
by Taosikai
15:12
created

Uploader::validateUploadedFile()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 11
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 7
nc 3
nop 1
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 with the same name
16
     * @var boolean
17
     */
18
    protected $override = false;
19
20
    /**
21
     * The saved path of the uploaded files
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
     * The rules collection
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
     * Sets override mode
52
     * @param boolean $override
53
     */
54
    public function setOverride($override)
55
    {
56
        $this->override = $override;
57
    }
58
59
    /**
60
     * Gets whether to enabled override mode
61
     * @return boolean
62
     */
63
    public function getOverride()
64
    {
65
        return $this->override;
66
    }
67
68
    /**
69
     * Set saved path for uploaded files
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 the saved path of uploaded files
86
     * @return string
87
     */
88
    public function getSavePath()
89
    {
90
        return $this->savePath;
91
    }
92
93
    /**
94
     * Sets whether to enable rand-name mode
95
     * @param $result
96
     */
97
    public function setRandName($result)
98
    {
99
        $this->isRandName = (boolean)$result;
100
    }
101
102
    /**
103
     * Sets whether to enable rand-name mode
104
     * @param boolean $result
105
     * @deprecated Use "setRandName" instead
106
     */
107
    public function setIsRandName($result)
108
    {
109
        $this->setRandName($result);
110
    }
111
112
    /**
113
     * Checks whether the rand-name mode is used
114
     * @return boolean
115
     */
116
    public function isRandName()
117
    {
118
        return $this->isRandName;
119
    }
120
121
    /**
122
     * Checks whether the rand-name mode is used
123
     * @return boolean
124
     * @deprecated Use "isRandName" instead
125
     */
126
    public function getIsRandName()
127
    {
128
        return $this->isRandName();
129
    }
130
131
    /**
132
     * Set filename generator
133
     * @param callable $generator
134
     */
135
    public function setFilenameGenerator(callable $generator)
136
    {
137
        $this->filenameGenerator = $generator;
138
    }
139
140
    /**
141
     * Gets the current filename generator
142
     * @return string
143
     */
144
    public function getFilenameGenerator()
145
    {
146
        if (is_null($this->filenameGenerator)) {
147
            $this->filenameGenerator = $this->isRandName ? $this->makeDefaultRandFilenameGenerator()
148
                : $this->makeDefaultOriginFilenameGenerator();
149
        }
150
        return $this->filenameGenerator;
151
    }
152
153
    /**
154
     * Add a rule
155
     * @param RuleInterface $rule
156
     */
157
    public function addRule(RuleInterface $rule)
158
    {
159
        $this->rules[] = $rule;
160
    }
161
162
    /**
163
     * Gets all rules
164
     * @return array
165
     */
166
    public function getRules()
167
    {
168
        return (array)$this->rules;
169
    }
170
171
    /**
172
     * Process upload
173
     * @param array $files
174
     * @throws UploadException
175
     * @return FileInfo|FileInfo[];
0 ignored issues
show
Documentation introduced by
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...
176
     */
177
    public function process($files)
178
    {
179
        if (empty($files)) {
180
            throw new UploadException('File array is not valid');
181
        }
182
        //multi files
183
        if (is_array($files['name'])) {
184
            $_files = [];
185
            foreach ($files['name'] as $key => $fileName) {
186
                $file = array(
187
                    'error' => $files['error'][$key],
188
                    'name' => $fileName,
189
                    'size' => $files['size'][$key],
190
                    'tmp_name' => $files['tmp_name'][$key],
191
                    'type' => $files['type'][$key]
192
                );
193
                $_files[] = $this->processUpload($file);
194
            }
195
            return $_files;
196
        } else {
197
            return $this->processUpload($files);
198
        }
199
    }
200
201
    /**
202
     * Process the uploaded file
203
     * @param array $info
204
     * @return FileInfo;
0 ignored issues
show
Documentation introduced by
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...
205
     */
206
    protected function processUpload(array $info)
207
    {
208
        $file = FileInfo::fromArray($info);
209
        if ($this->validateUploadedFile($file)) {
210
            $newFilePath = $this->generateFilename($file);
211
            $result = $this->moveUploadedFile($file, $newFilePath);
212
            if ($result) {
213
                $file->setPath($newFilePath);
214
                $file->setHasError(false);
215
            } else {
216
                $file->setHasError(true);
217
            }
218
        }
219
        return $file;
220
    }
221
222
    /**
223
     * Validates the uploaded file
224
     * @param FileInfo $file
225
     * @return boolean
226
     */
227
    protected function validateUploadedFile(FileInfo $file)
228
    {
229
        foreach ($this->rules as $rule) {
230
            if (!$rule->validate($file)) {
231
                $file->setErrorCode($rule->getErrorCode());
232
                $file->setErrorMsg($rule->getErrorMsg());
233
                return false;
234
            }
235
        }
236
        return true;
237
    }
238
239
    /**
240
     * Moves the uploaded file
241
     * Illegal upload files and other unsolicited causes can not be moved to throw an exception
242
     * @param FileInfo $file
243
     * @param string $newFilePath
244
     * @return boolean
245
     * @throws UploadException
246
     */
247
    protected function moveUploadedFile(FileInfo $file, $newFilePath)
248
    {
249
        $tmpName = $file->getTmpName();
250
        if (is_uploaded_file($tmpName)) {
251
            if (!file_exists($newFilePath) || $this->override) {
252
                if (!@move_uploaded_file($tmpName, $newFilePath)) {
253
                    throw new UploadException('Failed to move file');
254
                }
255
                return true;
256
            } else {
257
                $file->setErrorCode(ErrorStore::ERROR_SAME_NAME_FILE);
258
                $file->setErrorMsg(sprintf('File "%s" already exists', $file->getOriginName()));
259
                return false;
260
            }
261
        }
262
        throw new UploadException('The uploaded file is invalid');
263
    }
264
265
    /**
266
     * Makes default rand filename generator
267
     * @return callable
268
     */
269
    protected function makeDefaultRandFilenameGenerator()
270
    {
271
        return function (FileInfo $file) {
272
            return $this->savePath . time() . rand(10, 99) . '.' . $file->getExtension();
273
        };
274
    }
275
276
    /**
277
     * Makes default origin filename generator
278
     * @return callable
279
     */
280
    protected function makeDefaultOriginFilenameGenerator()
281
    {
282
        return function (FileInfo $file) {
283
            return $this->savePath . $file->getOriginName();
284
        };
285
    }
286
287
    /**
288
     * Generates an new file path
289
     * @param FileInfo $file
290
     * @return string
291
     */
292
    protected function generateFilename(FileInfo $file)
293
    {
294
        $generator = $this->getFilenameGenerator();
295
        $path = call_user_func($generator, $file);
296
        return $path;
297
    }
298
}
299