This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * Upload Manager. |
||
4 | * |
||
5 | * This file contains upload manager. |
||
6 | * |
||
7 | * @author Martin Stolz <[email protected]> |
||
8 | */ |
||
9 | |||
10 | namespace herroffizier\yii2um; |
||
11 | |||
12 | use Yii; |
||
13 | use yii\base\Component; |
||
14 | use yii\base\InvalidParamException; |
||
15 | use yii\web\UploadedFile; |
||
16 | use yii\helpers\FileHelper; |
||
17 | |||
18 | class UploadManager extends Component |
||
19 | { |
||
20 | /** |
||
21 | * Throw exception when trying to overwrite existing file. |
||
22 | */ |
||
23 | const STRATEGY_KEEP = 0; |
||
24 | |||
25 | /** |
||
26 | * Overwrite existing file silently. |
||
27 | */ |
||
28 | const STRATEGY_OVERWRITE = 1; |
||
29 | |||
30 | /** |
||
31 | * Rename new file if file with same name exists. |
||
32 | */ |
||
33 | const STRATEGY_RENAME = 2; |
||
34 | |||
35 | /** |
||
36 | * Path to upload folder. |
||
37 | * |
||
38 | * @var string |
||
39 | */ |
||
40 | public $uploadDir = '@webroot/upload'; |
||
41 | |||
42 | /** |
||
43 | * URL to upload folder. |
||
44 | * |
||
45 | * @var string |
||
46 | */ |
||
47 | public $uploadUrl = '@web/upload'; |
||
48 | |||
49 | /** |
||
50 | * Generate partition name based on file name. |
||
51 | * |
||
52 | * @param string $name |
||
53 | * |
||
54 | * @return string |
||
55 | */ |
||
56 | 20 | protected function getPartitionName($name) |
|
57 | { |
||
58 | 20 | return substr(md5(mb_substr($name, 0, 2)), 0, 2); |
|
59 | } |
||
60 | |||
61 | /** |
||
62 | * Add partition folder to given path. |
||
63 | * |
||
64 | * @param string $path |
||
65 | * @param string $name |
||
66 | * |
||
67 | * @return string |
||
68 | */ |
||
69 | 20 | protected function getPartitionedPath($path, $name) |
|
70 | { |
||
71 | 20 | $subfolder = $this->getPartitionName($name); |
|
72 | 20 | $path = FileHelper::normalizePath($path).'/'.$subfolder; |
|
73 | |||
74 | 20 | return $path; |
|
75 | } |
||
76 | |||
77 | /** |
||
78 | * Get prefixed path. |
||
79 | * |
||
80 | * @param string $path |
||
81 | * @param string $prefix |
||
82 | * |
||
83 | * @return string |
||
0 ignored issues
–
show
|
|||
84 | */ |
||
85 | 36 | protected function getPrefixedPath($path, $prefix) |
|
86 | { |
||
87 | 36 | if (substr($path, 0, 1) !== '/') { |
|
88 | 36 | $path = '/'.$path; |
|
89 | 27 | } |
|
90 | 36 | $path = FileHelper::normalizePath($path); |
|
91 | 36 | $prefixedPath = Yii::getAlias($prefix.$path); |
|
92 | |||
93 | 36 | return $prefixedPath; |
|
94 | } |
||
95 | |||
96 | /** |
||
97 | * Add index to file name. |
||
98 | * |
||
99 | * @param string $name |
||
100 | * @param int $index |
||
101 | * |
||
102 | * @return string |
||
103 | */ |
||
104 | 4 | protected function addIndexToName($name, $index) |
|
105 | { |
||
106 | 4 | $pathinfo = pathinfo($name); |
|
107 | |||
108 | 4 | if (empty($pathinfo['extension'])) { |
|
109 | 4 | return $pathinfo['basename'].'-'.$index; |
|
110 | } else { |
||
111 | 4 | return $pathinfo['filename'].'-'.$index.'.'.$pathinfo['extension']; |
|
112 | } |
||
113 | } |
||
114 | |||
115 | /** |
||
116 | * Pick up file name according to overwrite strategy and create path. |
||
117 | * |
||
118 | * @throws InvalidParamException when file cannot be created. |
||
119 | * |
||
120 | * @param string $path |
||
121 | * @param string $name |
||
122 | * @param int $overwriteStrategy |
||
123 | * |
||
124 | * @return string |
||
125 | */ |
||
126 | 16 | protected function createFilePath($path, $name, $overwriteStrategy) |
|
127 | { |
||
128 | 16 | $partitionedPath = $this->getPartitionedPath($path, $name); |
|
129 | 16 | $absolutePath = $this->getAbsolutePath($partitionedPath); |
|
130 | |||
131 | 16 | if (file_exists($absolutePath.'/'.$name)) { |
|
132 | switch ($overwriteStrategy) { |
||
133 | 4 | case self::STRATEGY_KEEP: |
|
134 | // File overwrtiting is forbidden. |
||
135 | 4 | throw new InvalidParamException('File '.$name.' already exists in '.$path.'.'); |
|
136 | |||
137 | 4 | case self::STRATEGY_RENAME: |
|
138 | 4 | $index = 0; |
|
139 | do { |
||
140 | 4 | ++$index; |
|
141 | 4 | $indexedName = $this->addIndexToName($name, $index); |
|
142 | |||
143 | 4 | $partitionedPath = $this->getPartitionedPath($path, $name); |
|
144 | 4 | $absolutePath = $this->getAbsolutePath($partitionedPath); |
|
145 | 4 | } while (file_exists($absolutePath.'/'.$indexedName)); |
|
146 | 4 | $name = $indexedName; |
|
147 | 4 | break; |
|
148 | |||
149 | 4 | case self::STRATEGY_OVERWRITE: |
|
150 | 4 | if (is_dir($absolutePath.'/'.$name)) { |
|
151 | // Cannot overwrtite folder. |
||
152 | 4 | throw new InvalidParamException($path.'/'.$name.' is a directory and cannot be overwritten.'); |
|
153 | } |
||
154 | 4 | break; |
|
155 | } |
||
156 | 3 | } |
|
157 | |||
158 | 16 | $this->createPath($partitionedPath); |
|
159 | |||
160 | 16 | return $partitionedPath.'/'.$name; |
|
161 | } |
||
162 | |||
163 | /** |
||
164 | * Get relative URL for relative path. |
||
165 | * |
||
166 | * @param string $path |
||
167 | * |
||
168 | * @return string |
||
0 ignored issues
–
show
|
|||
169 | */ |
||
170 | 4 | public function getUrl($path) |
|
171 | { |
||
172 | 4 | return $this->getPrefixedPath($path, $this->uploadUrl); |
|
173 | } |
||
174 | |||
175 | /** |
||
176 | * Get absolute path for relative path. |
||
177 | * |
||
178 | * @param string $path |
||
179 | * |
||
180 | * @return string |
||
0 ignored issues
–
show
|
|||
181 | */ |
||
182 | 32 | public function getAbsolutePath($path) |
|
183 | { |
||
184 | 32 | return $this->getPrefixedPath($path, $this->uploadDir); |
|
185 | } |
||
186 | |||
187 | /** |
||
188 | * Create given folder tree in upload folder. |
||
189 | * |
||
190 | * Returns absolute path of given path. |
||
191 | * |
||
192 | * @throws InvalidParamException when file cannot be created. |
||
193 | * |
||
194 | * @param string $path |
||
195 | * |
||
196 | * @return string |
||
0 ignored issues
–
show
|
|||
197 | */ |
||
198 | 24 | public function createPath($path) |
|
199 | { |
||
200 | 24 | $absolutePath = $this->getAbsolutePath($path); |
|
201 | 24 | if (!file_exists($absolutePath)) { |
|
202 | // FIXME Check return value. |
||
0 ignored issues
–
show
|
|||
203 | 24 | FileHelper::createDirectory($absolutePath); |
|
0 ignored issues
–
show
It seems like
$absolutePath defined by $this->getAbsolutePath($path) on line 200 can also be of type boolean ; however, yii\helpers\BaseFileHelper::createDirectory() does only seem to accept string , maybe add an additional type check?
If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check: /**
* @return array|string
*/
function returnsDifferentValues($x) {
if ($x) {
return 'foo';
}
return array();
}
$x = returnsDifferentValues($y);
if (is_array($x)) {
// $x is an array.
}
If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue. ![]() |
|||
204 | 22 | } elseif (!is_dir($absolutePath)) { |
|
205 | 4 | throw new InvalidParamException($path.' is a file, cannot create folder with the same name.'); |
|
206 | } |
||
207 | |||
208 | 24 | return $absolutePath; |
|
209 | } |
||
210 | |||
211 | /** |
||
212 | * Create folder tree appended with partition folder in upload folder. |
||
213 | * |
||
214 | * Partition folder name depends on given file name. |
||
215 | * |
||
216 | * Returns absolute path of given path. |
||
217 | * |
||
218 | * @param string $path |
||
219 | * @param string $name |
||
220 | * |
||
221 | * @return string |
||
0 ignored issues
–
show
|
|||
222 | */ |
||
223 | 4 | public function createPartitionedPath($path, $name) |
|
224 | { |
||
225 | 4 | $path = $this->getPartitionedPath($path, $name); |
|
226 | |||
227 | 4 | return $this->createPath($path); |
|
228 | } |
||
229 | |||
230 | /** |
||
231 | * Whether file with given relative path exists. |
||
232 | * |
||
233 | * @param string $filePath |
||
234 | * |
||
235 | * @return bool |
||
236 | */ |
||
237 | 4 | public function exists($filePath) |
|
238 | { |
||
239 | 4 | $absoluteFilePath = $this->getAbsolutePath($filePath); |
|
240 | |||
241 | 4 | return file_exists($absoluteFilePath); |
|
242 | } |
||
243 | |||
244 | /** |
||
245 | * Save data stored in $content as file to $path/$name in upload folder. |
||
246 | * |
||
247 | * Returns relative path with partition folder. |
||
248 | * |
||
249 | * @param string $path |
||
250 | * @param string $name |
||
251 | * @param string $content |
||
252 | * @param int[optional] $overwriteStrategy |
||
253 | * |
||
254 | * @return string |
||
255 | */ |
||
256 | 4 | View Code Duplication | public function saveContent($path, $name, $content, $overwriteStrategy = self::STRATEGY_KEEP) |
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
257 | { |
||
258 | 4 | $filePath = $this->createFilePath($path, $name, $overwriteStrategy); |
|
259 | 4 | $absoluteFilePath = $this->getAbsolutePath($filePath); |
|
260 | |||
261 | 4 | file_put_contents($absoluteFilePath, $content); |
|
262 | 4 | unset($content); |
|
263 | |||
264 | 4 | return $filePath; |
|
265 | } |
||
266 | |||
267 | /** |
||
268 | * Save $upload file to $path in upload folder. |
||
269 | * |
||
270 | * Returns relative path with partition folder. |
||
271 | * |
||
272 | * @param string $path |
||
273 | * @param UploadedFile $upload |
||
274 | * @param int[optional] $overwriteStrategy |
||
275 | * |
||
276 | * @return string |
||
277 | */ |
||
278 | 4 | View Code Duplication | public function saveUpload($path, UploadedFile $upload, $overwriteStrategy = self::STRATEGY_KEEP) |
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
279 | { |
||
280 | 4 | $filePath = $this->createFilePath($path, $upload->name, $overwriteStrategy); |
|
281 | 4 | $absoluteFilePath = $this->getAbsolutePath($filePath); |
|
282 | |||
283 | 4 | $upload->saveAs($absoluteFilePath); |
|
0 ignored issues
–
show
It seems like
$absoluteFilePath defined by $this->getAbsolutePath($filePath) on line 281 can also be of type boolean ; however, yii\web\UploadedFile::saveAs() does only seem to accept string , maybe add an additional type check?
If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check: /**
* @return array|string
*/
function returnsDifferentValues($x) {
if ($x) {
return 'foo';
}
return array();
}
$x = returnsDifferentValues($y);
if (is_array($x)) {
// $x is an array.
}
If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue. ![]() |
|||
284 | |||
285 | 4 | return $filePath; |
|
286 | } |
||
287 | |||
288 | /** |
||
289 | * Move or copy file. |
||
290 | * |
||
291 | * @param string $path |
||
292 | * @param string $absoluteFilePath |
||
293 | * @param int[optional] $overwriteStrategy |
||
294 | * @param string $function |
||
295 | * |
||
296 | * @return string |
||
297 | */ |
||
298 | 8 | View Code Duplication | protected function saveFileInternal($path, $absoluteFilePath, $overwriteStrategy, $function) |
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
299 | { |
||
300 | 8 | $filePath = $this->createFilePath($path, pathinfo($absoluteFilePath, PATHINFO_BASENAME), $overwriteStrategy); |
|
301 | 8 | $newAbsoluteFilePath = $this->getAbsolutePath($filePath); |
|
302 | |||
303 | 8 | $function($absoluteFilePath, $newAbsoluteFilePath); |
|
304 | |||
305 | 8 | return $filePath; |
|
306 | } |
||
307 | |||
308 | /** |
||
309 | * Save $absoluteFilePath file to $path in upload folder. |
||
310 | * |
||
311 | * Returns relative path with partition folder. |
||
312 | * |
||
313 | * @param string $path |
||
314 | * @param string $absoluteFilePath |
||
315 | * @param int[optional] $overwriteStrategy |
||
316 | * |
||
317 | * @return string |
||
318 | */ |
||
319 | 8 | public function saveFile($path, $absoluteFilePath, $overwriteStrategy = self::STRATEGY_KEEP) |
|
320 | { |
||
321 | 8 | return $this->saveFileInternal($path, $absoluteFilePath, $overwriteStrategy, 'copy'); |
|
322 | } |
||
323 | |||
324 | /** |
||
325 | * Move $absoluteFilePath file to $path in upload folder. |
||
326 | * |
||
327 | * Returns relative path with partition folder. |
||
328 | * |
||
329 | * @param string $path |
||
330 | * @param string $absoluteFilePath |
||
331 | * @param int[optional] $overwriteStrategy |
||
332 | * |
||
333 | * @return string |
||
334 | */ |
||
335 | 4 | public function moveFile($path, $absoluteFilePath, $overwriteStrategy = self::STRATEGY_KEEP) |
|
336 | { |
||
337 | 4 | return $this->saveFileInternal($path, $absoluteFilePath, $overwriteStrategy, 'rename'); |
|
338 | } |
||
339 | } |
||
340 |
This check compares the return type specified in the
@return
annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.