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 | * @link http://www.writesdown.com/ |
||
4 | * @copyright Copyright (c) 2015 WritesDown |
||
5 | * @license http://www.writesdown.com/license/ |
||
6 | */ |
||
7 | |||
8 | namespace common\components; |
||
9 | |||
10 | use common\models\Media; |
||
11 | use common\models\Post; |
||
12 | use Imagine\Image\Box; |
||
13 | use Imagine\Image\ManipulatorInterface; |
||
14 | use Imagine\Image\Point; |
||
15 | use Yii; |
||
16 | use yii\data\Pagination; |
||
17 | use yii\helpers\ArrayHelper; |
||
18 | use yii\helpers\FileHelper; |
||
19 | use yii\helpers\Url; |
||
20 | use yii\imagine\Image; |
||
21 | use yii\web\Response; |
||
22 | use yii\web\UploadedFile; |
||
23 | |||
24 | /** |
||
25 | * Upload handler for Media model. |
||
26 | * |
||
27 | * @author Agiel K. Saputra <[email protected]> |
||
28 | * @since 0.1.0 |
||
29 | */ |
||
30 | class MediaUploadHandler |
||
31 | { |
||
32 | const PRINT_RESPONSE = true; |
||
33 | const NOT_PRINT_RESPONSE = false; |
||
34 | |||
35 | /** |
||
36 | * @var array Options for upload handler, can be overridden over class constructs. |
||
37 | */ |
||
38 | protected $options = []; |
||
39 | /** |
||
40 | * @var array Used to generate response. |
||
41 | */ |
||
42 | protected $response = []; |
||
43 | /** |
||
44 | * @var array Grouping files based on its extension. |
||
45 | */ |
||
46 | protected $fileTypes = [ |
||
47 | 'image' => [ |
||
48 | 'extensions' => '/\.(gif|jpg|jpeg|png)$/i', |
||
49 | ], |
||
50 | 'audio' => [ |
||
51 | 'extensions' => '/\.(m4a|mp3|wav|wma|oga)$/i', |
||
52 | 'mime_icon' => 'img/mime/audio.png', |
||
53 | ], |
||
54 | 'video' => [ |
||
55 | 'extensions' => '/\.(3gp|mkv|flv|og?(a|g)|avi|mov|wmv|mp4|m4p|mp?(g|2|eg|e|v))$/i', |
||
56 | 'mime_icon' => 'img/mime/video.png', |
||
57 | ], |
||
58 | 'pdf' => [ |
||
59 | 'extensions' => '/\.(pdf|xps)$/i', |
||
60 | 'mime_icon' => 'img/mime/pdf.png', |
||
61 | ], |
||
62 | 'spreadsheet' => [ |
||
63 | 'extensions' => '/\.(xls|xlsx|ods|csv|xml)$/i', |
||
64 | 'mime_icon' => 'img/mime/spreadsheet.png', |
||
65 | ], |
||
66 | 'document' => [ |
||
67 | 'extensions' => '/\.(doc?(m|x)|odt)$/i', |
||
68 | 'mime_icon' => 'img/mime/document.png', |
||
69 | ], |
||
70 | 'archive' => [ |
||
71 | 'extensions' => '/\.(rar|zip|tar|7zip)$/i', |
||
72 | 'mime_icon' => 'img/mime/archive.png', |
||
73 | ], |
||
74 | 'code' => [ |
||
75 | 'extensions' => '/\.(php|c?pp|java|vb?s|html|js|css)$/i', |
||
76 | 'mime_icon' => 'img/mime/audio.png', |
||
77 | ], |
||
78 | 'interactive' => [ |
||
79 | 'extensions' => '/\.(ppt|pptx|odp)$/i', |
||
80 | 'icon' => 'img/mime/interactive.png', |
||
81 | ], |
||
82 | 'text' => [ |
||
83 | 'extensions' => '/\.(txt|md|bat)$/i', |
||
84 | 'mime_icon' => 'img/mime/text.png', |
||
85 | ], |
||
86 | ]; |
||
87 | |||
88 | /** |
||
89 | * @var Media |
||
90 | */ |
||
91 | private $_media; |
||
92 | /** |
||
93 | * @var array Used to create Media Meta. |
||
94 | */ |
||
95 | private $_meta; |
||
96 | |||
97 | /** |
||
98 | * Create object of MediaUploadHandler. |
||
99 | * |
||
100 | * @param array|null $options |
||
101 | * @param bool $initialize |
||
102 | */ |
||
103 | public function __construct($options = null, $initialize = true) |
||
104 | { |
||
105 | // Set response format to RAW. |
||
106 | Yii::$app->response->format = Response::FORMAT_RAW; |
||
107 | // Set options of MediaUploadHandler. |
||
108 | $this->setOptions($options); |
||
0 ignored issues
–
show
|
|||
109 | |||
110 | if ($initialize) { |
||
111 | $this->initialize(); |
||
112 | } |
||
113 | } |
||
114 | |||
115 | /** |
||
116 | * Initialize the action of MediaUploadHandler based on request method if set true. |
||
117 | */ |
||
118 | protected function initialize() |
||
119 | { |
||
120 | switch (Yii::$app->request->method) { |
||
121 | case 'OPTIONS': |
||
122 | case 'HEAD': |
||
123 | $this->head(); |
||
124 | break; |
||
125 | case 'PATCH': |
||
126 | case 'PUT': |
||
127 | case 'POST': |
||
128 | $this->post($this->getOption('print_response')); |
||
0 ignored issues
–
show
$this->getOption('print_response') is of type string|array|null , but the function expects a boolean .
It seems like the type of the argument is not accepted by the function/method which you are calling. In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug. We suggest to add an explicit type cast like in the following example: function acceptsInteger($int) { }
$x = '123'; // string "123"
// Instead of
acceptsInteger($x);
// we recommend to use
acceptsInteger((integer) $x);
![]() |
|||
129 | break; |
||
130 | case 'GET': |
||
131 | $this->get($this->getOption('print_response')); |
||
0 ignored issues
–
show
It seems like
$this->getOption('print_response') targeting common\components\MediaUploadHandler::getOption() can also be of type array or string ; however, common\components\MediaUploadHandler::get() does only seem to accept integer|null , maybe add an additional type check?
This check looks at variables that are passed out again to other methods. If the outgoing method call has stricter type requirements than the method itself, an issue is raised. An additional type check may prevent trouble. ![]() |
|||
132 | break; |
||
133 | case 'DELETE': |
||
134 | $this->delete($this->getOption('print_response')); |
||
0 ignored issues
–
show
$this->getOption('print_response') is of type string|array|null , but the function expects a integer .
It seems like the type of the argument is not accepted by the function/method which you are calling. In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug. We suggest to add an explicit type cast like in the following example: function acceptsInteger($int) { }
$x = '123'; // string "123"
// Instead of
acceptsInteger($x);
// we recommend to use
acceptsInteger((integer) $x);
![]() |
|||
135 | break; |
||
136 | default: |
||
137 | $this->setHeader('HTTP/1.1 405 Method Not Allowed'); |
||
138 | } |
||
139 | } |
||
140 | |||
141 | /** |
||
142 | * Get server var based on id. Return null when it's not exist. |
||
143 | * |
||
144 | * @param $id |
||
145 | * @return mixed |
||
146 | */ |
||
147 | protected function getServerVar($id) |
||
0 ignored issues
–
show
getServerVar uses the super-global variable $_SERVER which is generally not recommended.
Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable: // Bad
class Router
{
public function generate($path)
{
return $_SERVER['HOST'].$path;
}
}
// Better
class Router
{
private $host;
public function __construct($host)
{
$this->host = $host;
}
public function generate($path)
{
return $this->host.$path;
}
}
class Controller
{
public function myAction(Request $request)
{
// Instead of
$page = isset($_GET['page']) ? intval($_GET['page']) : 1;
// Better (assuming you use the Symfony2 request)
$page = $request->query->get('page', 1);
}
}
![]() |
|||
148 | { |
||
149 | if (isset($_SERVER[$id])) { |
||
150 | return $_SERVER[$id]; |
||
151 | } |
||
152 | |||
153 | return null; |
||
154 | } |
||
155 | |||
156 | /** |
||
157 | * Get singular param name. |
||
158 | * |
||
159 | * @return string |
||
160 | */ |
||
161 | protected function getSingularParamName() |
||
162 | { |
||
163 | return substr($this->options['param_name'], 0, -1); |
||
164 | } |
||
165 | |||
166 | /** |
||
167 | * Adds a new header. |
||
168 | * If there is already a header with the same name, it will be replaced. |
||
169 | * |
||
170 | * @param string $name The name of the header. |
||
171 | * @param string $value The value of the header. |
||
172 | */ |
||
173 | protected function setHeader($name, $value = '') |
||
174 | { |
||
175 | Yii::$app->response->headers->set($name, $value); |
||
176 | } |
||
177 | |||
178 | /** |
||
179 | * Set header content-type. |
||
180 | */ |
||
181 | protected function sendContentTypeHeader() |
||
182 | { |
||
183 | $this->setHeader('Vary', 'Accept'); |
||
184 | |||
185 | if (strpos($this->getServerVar('HTTP_ACCEPT'), 'application/json') !== false) { |
||
186 | $this->setHeader('Content-type', 'application/json'); |
||
187 | } else { |
||
188 | $this->setHeader('Content-type', 'text/plain'); |
||
189 | } |
||
190 | } |
||
191 | |||
192 | /** |
||
193 | * Set header Access-Control-*. |
||
194 | */ |
||
195 | protected function sendAccessControlHeaders() |
||
196 | { |
||
197 | $this->setHeader('Access-Control-Allow-Origin', $this->options['access_control_allow_origin']); |
||
198 | $this->setHeader('Access-Control-Allow-Credentials', $this->options['access_control_allow_credentials'] |
||
199 | ? 'true' |
||
200 | : 'false' |
||
201 | ); |
||
202 | $this->setHeader('Access-Control-Allow-Methods', implode(', ', $this->options['access_control_allow_methods'])); |
||
203 | $this->setHeader('Access-Control-Allow-Headers', implode(', ', $this->options['access_control_allow_headers'])); |
||
204 | } |
||
205 | |||
206 | /** |
||
207 | * Finds the Media model based on its primary key value. |
||
208 | * If the model is not found it will return null. |
||
209 | * |
||
210 | * @param integer $id |
||
211 | * @return Media|array |
||
212 | */ |
||
213 | protected function findMedia($id) |
||
214 | { |
||
215 | if (($model = Media::findOne($id)) !== null) { |
||
216 | return $model; |
||
217 | } |
||
218 | |||
219 | return null; |
||
220 | } |
||
221 | |||
222 | /** |
||
223 | * Finds the Post model based on its primary key value. |
||
224 | * If the model is not found it will return null. |
||
225 | * |
||
226 | * @param integer $id |
||
227 | * @return Post|null |
||
228 | */ |
||
229 | protected function findPost($id) |
||
230 | { |
||
231 | if (($model = Post::findOne($id)) !== null) { |
||
232 | return $model; |
||
233 | } |
||
234 | |||
235 | return null; |
||
236 | } |
||
237 | |||
238 | /** |
||
239 | * Get user path of login user. It can be disabled by override config, set user_dirs to false. |
||
240 | * |
||
241 | * @return string The username of login user. |
||
242 | */ |
||
243 | protected function getUserPath() |
||
244 | { |
||
245 | if ($this->options['user_dirs'] && !Yii::$app->user->isGuest) { |
||
246 | return Yii::$app->user->identity->username . '/'; |
||
247 | } |
||
248 | |||
249 | return ''; |
||
250 | } |
||
251 | |||
252 | /** |
||
253 | * Year-month path generated by date function can be disable by set year_month_path to false. |
||
254 | * |
||
255 | * @return string date(/Y/m). |
||
256 | */ |
||
257 | protected function getYearMonthPath() |
||
258 | { |
||
259 | if ($this->options['year_month_dirs']) { |
||
260 | return date('Y/m/'); |
||
261 | } |
||
262 | |||
263 | return ''; |
||
264 | } |
||
265 | |||
266 | /** |
||
267 | * Get upload path based on current config, generate upload_dir/user_path/y/m/filename.ext. |
||
268 | * |
||
269 | * @param null $fileName Filename and extension (filename.ext). |
||
270 | * @return string |
||
271 | */ |
||
272 | protected function getUploadPath($fileName = null) |
||
273 | { |
||
274 | return $this->getOption('upload_dir') . $this->getUserPath() . $this->getYearMonthPath() . $fileName; |
||
275 | } |
||
276 | |||
277 | /** |
||
278 | * Get file-path of the filename. |
||
279 | * |
||
280 | * @param string|null $fileName |
||
281 | * @return string |
||
282 | */ |
||
283 | protected function getFilePath($fileName = null) |
||
284 | { |
||
285 | return $this->getOption('upload_dir') . $fileName; |
||
286 | } |
||
287 | |||
288 | /** |
||
289 | * Generate slug for uploaded file. |
||
290 | * Replace all space to - and transform all character to lowercase. |
||
291 | * |
||
292 | * @param string $fileName |
||
293 | * @param array $replace The replace_pairs parameter may be used as a substitute for to and from in which case. |
||
294 | * it's an array in the form array('from' => 'to', ...). |
||
295 | * @param string $delimiter |
||
296 | * @see strtr |
||
297 | * @return string Clean name |
||
298 | */ |
||
299 | protected function generateSlug($fileName, $replace = [], $delimiter = '-') |
||
300 | { |
||
301 | setlocale(LC_ALL, 'en_US.UTF8'); |
||
302 | $fileName = trim($fileName); |
||
303 | |||
304 | if (!empty($replace)) { |
||
305 | $fileName = strtr($fileName, $replace); |
||
306 | } |
||
307 | |||
308 | $cleanName = iconv('UTF-8', 'ASCII//TRANSLIT', $fileName); |
||
309 | $cleanName = preg_replace("/[^a-zA-Z0-9\/_|+ -]/", '', $cleanName); |
||
310 | $cleanName = strtolower(trim($cleanName, '-')); |
||
311 | $cleanName = preg_replace("/[\/_|+ -]+/", $delimiter, $cleanName); |
||
312 | |||
313 | return $cleanName; |
||
314 | } |
||
315 | |||
316 | /** |
||
317 | * Callback function of upCountName. |
||
318 | * |
||
319 | * @param array $matches |
||
320 | * @return string |
||
321 | */ |
||
322 | protected function upCountNameCallback($matches) |
||
323 | { |
||
324 | $index = isset($matches[1]) ? intval($matches[1]) + 1 : 1; |
||
325 | $ext = isset($matches[2]) ? $matches[2] : ''; |
||
326 | |||
327 | return '-' . $index . $ext; |
||
328 | } |
||
329 | |||
330 | /** |
||
331 | * The number before fileName extension is replaced by upCountNameCallback. |
||
332 | * |
||
333 | * @param string $fileName |
||
334 | * @return mixed |
||
335 | * @see upCountNameCallback |
||
336 | */ |
||
337 | protected function upCountName($fileName) |
||
338 | { |
||
339 | return preg_replace_callback('/(?:(?:\-([\d]+))?(\.[^.]+))?$/', [$this, 'upCountNameCallback'], $fileName, 1); |
||
340 | } |
||
341 | |||
342 | /** |
||
343 | * Get filename of uploaded file. |
||
344 | * If the filename is already exist in the upload directory |
||
345 | * then the number between - and before the extension plus 1. |
||
346 | * |
||
347 | * @param UploadedFile $file |
||
348 | * @return string |
||
349 | */ |
||
350 | protected function getFileName($file) |
||
351 | { |
||
352 | $index = 0; |
||
353 | $fileName = $this->generateSlug($file->baseName); |
||
354 | $fileName .= '.' . $file->extension; |
||
355 | $fileName = trim(basename(stripslashes($fileName)), ".\x00..\x20"); |
||
356 | |||
357 | if (!$fileName) { |
||
358 | $fileName = str_replace('.', '-', microtime(true)); |
||
359 | } |
||
360 | |||
361 | while (is_file($this->getUploadPath($fileName))) { |
||
362 | $fileName = $this->upCountName($fileName); |
||
363 | $index++; |
||
364 | } |
||
365 | |||
366 | if ($index !== 0) { |
||
367 | // Replace media title |
||
368 | $this->_media->title = $file->baseName . ' ' . $index; |
||
369 | } |
||
370 | |||
371 | return $fileName; |
||
372 | } |
||
373 | |||
374 | /** |
||
375 | * @param \imagine\image\ImageInterface $image |
||
376 | * @param string $filePath |
||
377 | * @return bool |
||
378 | */ |
||
379 | protected function correctExifRotation($image, $filePath) |
||
380 | { |
||
381 | if (!function_exists('exif_read_data')) { |
||
382 | return false; |
||
383 | } |
||
384 | |||
385 | $exif = @exif_read_data($filePath); |
||
386 | |||
387 | if ($exif === false) { |
||
388 | return false; |
||
389 | } |
||
390 | |||
391 | $orientation = (int)@$exif['Orientation']; |
||
392 | |||
393 | if ($orientation < 2 || $orientation > 8) { |
||
394 | return false; |
||
395 | } |
||
396 | |||
397 | switch ($orientation) { |
||
398 | case 8: |
||
399 | $image->rotate(-90); |
||
400 | break; |
||
401 | case 3: |
||
402 | $image->rotate(180); |
||
403 | break; |
||
404 | case 6: |
||
405 | $image->rotate(90); |
||
406 | break; |
||
407 | } |
||
408 | |||
409 | return true; |
||
410 | } |
||
411 | |||
412 | /** |
||
413 | * @param $fileName |
||
414 | * @param $version |
||
415 | * @param $options |
||
416 | * @return bool|\imagine\Image\ManipulatorInterface |
||
417 | */ |
||
418 | protected function createScaledImage($fileName, $version, $options) |
||
419 | { |
||
420 | $success = false; |
||
421 | $filePath = $this->getFilePath($fileName); |
||
422 | $image = Image::getImagine()->open($filePath); |
||
423 | |||
424 | if ($this->getOption('correct_exif_rotation')) { |
||
425 | $this->correctExifRotation($image, $filePath); |
||
426 | } |
||
427 | |||
428 | $maxWidth = $imageWidth = $image->getSize()->getWidth(); |
||
429 | $maxHeight = $imageHeight = $image->getSize()->getHeight(); |
||
430 | |||
431 | if (!empty($options['max_width'])) { |
||
432 | $maxWidth = $options['max_width']; |
||
433 | } |
||
434 | |||
435 | if (!empty($options['max_height'])) { |
||
436 | $maxHeight = $options['max_height']; |
||
437 | } |
||
438 | |||
439 | $scale = min($maxWidth / $imageWidth, $maxHeight / $imageHeight); |
||
440 | |||
441 | if ($scale > 0 && $scale <= 1) { |
||
442 | if (empty($options['crop'])) { |
||
443 | $newWidth = round($imageWidth * $scale); |
||
444 | $newHeight = round($imageHeight * $scale); |
||
445 | $newFileName = substr($fileName, 0, -(strlen($this->_media->file->extension) + 1)) . |
||
446 | '-' . $newWidth . |
||
447 | 'x' . $newHeight . |
||
448 | '.' . $this->_media->file->extension; |
||
449 | $newFilePath = $this->getFilePath($newFileName); |
||
450 | $success = $image->thumbnail(new Box($newWidth, $newHeight)) |
||
451 | ->save($newFilePath); |
||
452 | View Code Duplication | if ($success) { |
|
0 ignored issues
–
show
This code seems to be duplicated across 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. ![]() |
|||
453 | $this->_meta['versions'][$version] = [ |
||
454 | 'url' => $newFileName, |
||
455 | 'width' => $newWidth, |
||
456 | 'height' => $newHeight, |
||
457 | ]; |
||
458 | } |
||
459 | } else { |
||
460 | if (($imageWidth / $imageHeight) >= ($maxWidth / $maxHeight)) { |
||
461 | $newWidth = round($imageWidth / ($imageHeight / $maxHeight)); |
||
462 | $newHeight = $maxHeight; |
||
463 | } else { |
||
464 | $newWidth = $maxWidth; |
||
465 | $newHeight = round($imageHeight / ($imageWidth / $maxWidth)); |
||
466 | } |
||
467 | $pointX = abs(round(($newWidth - $maxWidth) / 2)); |
||
468 | $pointY = abs(round(($newHeight - $maxHeight) / 2)); |
||
469 | $newFileName = substr($fileName, 0, -(strlen($this->_media->file->extension) + 1)) . |
||
470 | '-' . $maxWidth . |
||
471 | 'x' . $maxHeight . |
||
472 | '.' . $this->_media->file->extension; |
||
473 | $newFilePath = $this->getFilePath($newFileName); |
||
474 | $success = $image->thumbnail(new Box($newWidth, $newHeight), ManipulatorInterface::THUMBNAIL_OUTBOUND) |
||
475 | ->crop(new Point($pointX, $pointY), new Box($maxWidth, $maxHeight)) |
||
476 | ->save($newFilePath); |
||
477 | View Code Duplication | if ($success) { |
|
0 ignored issues
–
show
This code seems to be duplicated across 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. ![]() |
|||
478 | $this->_meta['versions'][$version] = [ |
||
479 | 'url' => $newFileName, |
||
480 | 'width' => $maxWidth, |
||
481 | 'height' => $maxWidth, |
||
482 | ]; |
||
483 | } |
||
484 | } |
||
485 | } |
||
486 | |||
487 | return $success; |
||
488 | } |
||
489 | |||
490 | /** |
||
491 | * Handle image file. |
||
492 | * |
||
493 | * @param string $fileName |
||
494 | */ |
||
495 | protected function handleImageFile($fileName) |
||
496 | { |
||
497 | if ($versions = $this->getOption('versions')) { |
||
498 | foreach ($versions as $version => $options) { |
||
0 ignored issues
–
show
The expression
$versions of type string|array is not guaranteed to be traversable. How about adding an additional type check?
There are different options of fixing this problem.
![]() |
|||
499 | $this->createScaledImage($fileName, $version, $options); |
||
500 | } |
||
501 | } |
||
502 | } |
||
503 | |||
504 | /** |
||
505 | * Set icon url. |
||
506 | * |
||
507 | * @param string $fileName |
||
508 | * @return string |
||
509 | */ |
||
510 | protected function setIconUrl($fileName) |
||
511 | { |
||
512 | foreach ($this->fileTypes as $name => $type) { |
||
513 | if (preg_match($type['extensions'], $fileName)) { |
||
514 | if ($name === 'image') { |
||
515 | return $this->_meta['versions']['thumbnail']['url']; |
||
516 | } |
||
517 | |||
518 | return $type['mime_icon']; |
||
519 | } |
||
520 | } |
||
521 | |||
522 | return 'img/mime/default.png'; |
||
523 | } |
||
524 | |||
525 | /** |
||
526 | * Handle uploaded file. If the uploaded file is valid image type, the file will be resize or crop |
||
527 | * based on versions in options. |
||
528 | * |
||
529 | * @param UploadedFile $file |
||
530 | */ |
||
531 | protected function handleFileUpload($file) |
||
532 | { |
||
533 | $this->_meta['filename'] = $this->getFileName($file); |
||
534 | $this->_meta['file_size'] = $file->size; |
||
535 | $uploadDir = $this->getUploadPath(); |
||
536 | $uploadPath = $this->getUploadPath($this->_meta['filename']); |
||
537 | |||
538 | if (!is_dir($uploadDir)) { |
||
539 | FileHelper::createDirectory($uploadDir, $this->getOption('mkdir_mode')); |
||
0 ignored issues
–
show
$this->getOption('mkdir_mode') is of type string|array|null , but the function expects a integer .
It seems like the type of the argument is not accepted by the function/method which you are calling. In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug. We suggest to add an explicit type cast like in the following example: function acceptsInteger($int) { }
$x = '123'; // string "123"
// Instead of
acceptsInteger($x);
// we recommend to use
acceptsInteger((integer) $x);
![]() |
|||
540 | } |
||
541 | |||
542 | if ($file->saveAs($uploadPath)) { |
||
543 | $this->_meta['versions']['full']['url'] = $this->getUserPath() |
||
544 | . $this->getYearMonthPath() |
||
545 | . $this->_meta['filename']; |
||
546 | |||
547 | if (preg_match($this->fileTypes['image']['extensions'], $this->_meta['filename'])) { |
||
548 | $image = Image::getImagine()->open($this->getFilePath($this->_meta['versions']['full']['url'])); |
||
549 | $this->handleImageFile($this->_meta['versions']['full']['url']); |
||
550 | $this->_meta['versions']['full']['width'] = $image->getSize()->getWidth(); |
||
551 | $this->_meta['versions']['full']['height'] = $image->getSize()->getHeight(); |
||
552 | } |
||
553 | |||
554 | $this->_meta['icon_url'] = $this->setIconUrl($this->_meta['filename']); |
||
555 | } |
||
556 | } |
||
557 | |||
558 | /** |
||
559 | * @return \yii\data\Pagination |
||
560 | */ |
||
561 | protected function getPages() |
||
562 | { |
||
563 | $query = Media::find()->orderBy(['id' => SORT_DESC]); |
||
564 | $pages = new Pagination([ |
||
565 | 'totalCount' => $query->count(), |
||
566 | 'pageSize' => $this->getOption('files_per_page'), |
||
567 | ]); |
||
568 | |||
569 | return $pages; |
||
570 | } |
||
571 | |||
572 | /** |
||
573 | * @param $pages Pagination |
||
574 | * @return array |
||
575 | */ |
||
576 | protected function getPaging($pages) |
||
577 | { |
||
578 | $currentPage = $pages->getPage(); |
||
579 | $perPage = $pages->getPageSize(); |
||
580 | $result = [ |
||
581 | 'next_url' => '', |
||
582 | 'current_page' => $currentPage, |
||
583 | 'per_page' => $perPage, |
||
584 | ]; |
||
585 | $pageCount = $pages->getPageCount(); |
||
586 | |||
587 | if ($currentPage + 1 < $pageCount) { |
||
588 | if (($page = $currentPage + 1) >= $pageCount - 1) { |
||
589 | $page = $pageCount - 1; |
||
590 | } |
||
591 | |||
592 | return [ |
||
593 | 'next_url' => Url::to(['get-json', 'page' => $page + 1, 'per-page' => $pages->getPageSize()]), |
||
594 | 'current_page' => $page, |
||
595 | 'per_page' => $perPage, |
||
596 | ]; |
||
597 | } |
||
598 | |||
599 | return $result; |
||
600 | } |
||
601 | |||
602 | /** |
||
603 | * Set options of upload Handler. |
||
604 | * |
||
605 | * @param array $options |
||
606 | */ |
||
607 | public function setOptions($options = []) |
||
608 | { |
||
609 | $this->options = [ |
||
610 | 'script_url' => Yii::$app->request->absoluteUrl, |
||
611 | 'upload_dir' => Yii::getAlias('@public/uploads/'), |
||
612 | 'upload_url' => Media::getUploadUrl(), |
||
613 | 'user_dirs' => true, |
||
614 | 'year_month_dirs' => true, |
||
615 | 'mkdir_mode' => 0755, |
||
616 | 'param_name' => 'files', |
||
617 | 'access_control_allow_origin' => '*', |
||
618 | 'access_control_allow_credentials' => false, |
||
619 | 'correct_exif_rotation' => true, |
||
620 | 'pagination_route' => '/media/get-json', |
||
621 | 'access_control_allow_methods' => [ |
||
622 | 'OPTIONS', |
||
623 | 'HEAD', |
||
624 | 'GET', |
||
625 | 'POST', |
||
626 | 'PUT', |
||
627 | 'PATCH', |
||
628 | 'DELETE', |
||
629 | ], |
||
630 | 'access_control_allow_headers' => [ |
||
631 | 'Content-Type', |
||
632 | 'Content-Range', |
||
633 | 'Content-Disposition', |
||
634 | ], |
||
635 | 'versions' => [ |
||
636 | 'large' => [ |
||
637 | 'max_width' => 1024, |
||
638 | 'max_height' => 1024, |
||
639 | ], |
||
640 | 'medium' => [ |
||
641 | 'max_width' => 300, |
||
642 | 'max_height' => 300, |
||
643 | ], |
||
644 | 'thumbnail' => [ |
||
645 | 'max_width' => 150, |
||
646 | 'max_height' => 150, |
||
647 | 'crop' => 1, |
||
648 | ], |
||
649 | ], |
||
650 | 'files_per_page' => 100, |
||
651 | 'print_response' => true, |
||
652 | ]; |
||
653 | |||
654 | if ($options) { |
||
0 ignored issues
–
show
The expression
$options of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent. Consider making the comparison explicit by using ![]() |
|||
655 | $this->options = ArrayHelper::merge($this->options, $options); |
||
656 | } |
||
657 | } |
||
658 | |||
659 | /** |
||
660 | * Get all of MediaUploadHandler Options. |
||
661 | */ |
||
662 | public function getOptions() |
||
663 | { |
||
664 | return $this->options; |
||
665 | } |
||
666 | |||
667 | /** |
||
668 | * Get single option of MediaUploadHandler. |
||
669 | * Return string or array if option exist, or return null if not exist. |
||
670 | * |
||
671 | * @param string $id |
||
672 | * @return string|array|null |
||
673 | */ |
||
674 | public function getOption($id) |
||
675 | { |
||
676 | if (isset($this->options[$id])) { |
||
677 | return $this->options[$id]; |
||
678 | } |
||
679 | |||
680 | return null; |
||
681 | } |
||
682 | |||
683 | /** |
||
684 | * Generate response based on Media primary key. |
||
685 | * |
||
686 | * @param Media $media |
||
687 | * @return array |
||
688 | */ |
||
689 | public function generateResponse($media) |
||
690 | { |
||
691 | $metadata = $media->getMeta('metadata'); |
||
692 | $response = ArrayHelper::merge(ArrayHelper::toArray($media), $metadata); |
||
693 | $response['date_formatted'] = Yii::$app->formatter->asDatetime($media->date); |
||
694 | $response['readable_size'] = Yii::$app->formatter->asShortSize($metadata['file_size']); |
||
695 | $response['delete_url'] = Url::to(['/media/ajax-delete', 'id' => $media->id, 'delete' => '1']); |
||
696 | $response['update_url'] = Url::to(['/media/update', 'id' => $media->id]); |
||
697 | $response['view_url'] = $media->getUrl(); |
||
698 | |||
699 | if (preg_match('/^image\//', $media->mime_type)) { |
||
700 | $response['type'] = 'image'; |
||
701 | $response['icon_url'] = $this->getOption('upload_url') . $metadata['icon_url']; |
||
702 | } else { |
||
703 | $response['icon_url'] = Yii::getAlias('@web') . '/' . $metadata['icon_url']; |
||
704 | if (preg_match('/^video\//', $media->mime_type)) { |
||
705 | $response['type'] = 'video'; |
||
706 | } elseif (preg_match('/^audio\//', $media->mime_type)) { |
||
707 | $response['type'] = 'audio'; |
||
708 | } else { |
||
709 | $response['type'] = 'file'; |
||
710 | } |
||
711 | } |
||
712 | |||
713 | return $response; |
||
714 | } |
||
715 | |||
716 | /** |
||
717 | * Set response. |
||
718 | * |
||
719 | * @param array $response |
||
720 | */ |
||
721 | public function setResponse($response) |
||
722 | { |
||
723 | $this->response = $response; |
||
724 | } |
||
725 | |||
726 | |||
727 | /** |
||
728 | * Generate response in the form of a string of json. |
||
729 | * |
||
730 | * @param bool $printResponse |
||
731 | * @return array |
||
732 | */ |
||
733 | public function getResponse($printResponse = self::PRINT_RESPONSE) |
||
734 | { |
||
735 | if ($printResponse) { |
||
736 | $this->head(); |
||
737 | $content = Json::encode($this->response); |
||
738 | $redirect = stripslashes(Yii::$app->request->getQueryParam('redirect')); |
||
739 | if ($redirect) { |
||
740 | $this->setHeader('Location', sprintf($redirect, rawurlencode($content))); |
||
741 | |||
742 | return null; |
||
743 | } |
||
744 | echo $content; |
||
745 | } |
||
746 | |||
747 | return $this->response; |
||
748 | } |
||
749 | |||
750 | /** |
||
751 | * Set response header. |
||
752 | */ |
||
753 | public function head() |
||
754 | { |
||
755 | $this->setHeader('Pragma', 'no-cache'); |
||
756 | $this->setHeader('Cache-Control', 'no-store, no-cache, must-revalidate'); |
||
757 | $this->setHeader('Content-Disposition', 'inline; filename="files.json"'); |
||
758 | // Prevent Internet Explorer from MIME-sniffing the content-type: |
||
759 | $this->setHeader('X-Content-Type-Options', 'nosniff'); |
||
760 | if ($this->options['access_control_allow_origin']) { |
||
761 | $this->sendAccessControlHeaders(); |
||
762 | } |
||
763 | $this->sendContentTypeHeader(); |
||
764 | } |
||
765 | |||
766 | /** |
||
767 | * Get media files. |
||
768 | * |
||
769 | * @param int $id |
||
770 | * @param bool $printResponse |
||
771 | * @return array |
||
772 | */ |
||
773 | public function get($id = null, $printResponse = self::PRINT_RESPONSE) |
||
774 | { |
||
775 | $content = []; |
||
776 | |||
777 | if ($id && $media = $this->findMedia($id)) { |
||
0 ignored issues
–
show
The expression
$id of type integer|null is loosely compared to true ; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.
In PHP, under loose comparison (like For 0 == false // true
0 == null // true
123 == false // false
123 == null // false
// It is often better to use strict comparison
0 === false // false
0 === null // false
![]() |
|||
778 | $response = [ |
||
779 | $this->getSingularParamName() => $this->generateResponse($media), |
||
0 ignored issues
–
show
$media is of type array|boolean , but the function expects a object<common\models\Media> .
It seems like the type of the argument is not accepted by the function/method which you are calling. In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug. We suggest to add an explicit type cast like in the following example: function acceptsInteger($int) { }
$x = '123'; // string "123"
// Instead of
acceptsInteger($x);
// we recommend to use
acceptsInteger((integer) $x);
![]() |
|||
780 | ]; |
||
781 | } else { |
||
782 | $query = Media::find()->orderBy(['id' => SORT_DESC]); |
||
783 | $pages = $this->getPages(); |
||
784 | |||
785 | $query->andFilterWhere(['like', 'post_id', Yii::$app->request->get('post')]) |
||
786 | ->andFilterWhere(['like', 'mime_type', Yii::$app->request->get('type')]) |
||
787 | ->andFilterWhere(['like', 'title', Yii::$app->request->get('keyword')]) |
||
788 | ->orFilterWhere(['like', 'content', Yii::$app->request->get('keyword')]); |
||
789 | |||
790 | if ($models = $query->offset($pages->offset)->limit($pages->limit)->all()) { |
||
791 | foreach ($models as $media) { |
||
792 | /* @var $media Media */ |
||
793 | $content[] = $this->generateResponse($media); |
||
794 | } |
||
795 | } |
||
796 | $response = [ |
||
797 | $this->getOption('param_name') => $content, |
||
798 | 'paging' => $this->getPaging($pages), |
||
799 | ]; |
||
800 | } |
||
801 | |||
802 | $this->setResponse($response); |
||
803 | |||
804 | return $this->getResponse($printResponse); |
||
805 | } |
||
806 | |||
807 | /** |
||
808 | * Upload file to server. |
||
809 | * |
||
810 | * @param bool $printResponse |
||
811 | * @return array |
||
812 | */ |
||
813 | public function post($printResponse = self::PRINT_RESPONSE) |
||
814 | { |
||
815 | if (Yii::$app->request->get('delete') && $id = Yii::$app->request->get('id')) { |
||
816 | return $this->delete($id, $printResponse); |
||
817 | } |
||
818 | |||
819 | $response = []; |
||
820 | $this->_media = new Media(); |
||
821 | $this->_media->file = UploadedFile::getInstance($this->_media, 'file'); |
||
822 | |||
823 | if ($this->_media->file !== null && $this->_media->validate(['file'])) { |
||
824 | if ($postId = Yii::$app->request->get('post')) { |
||
825 | $post = $this->findPost($postId); |
||
826 | $this->_media->post_id = $post->id; |
||
827 | } |
||
828 | |||
829 | $this->_media->title = $this->_media->file->baseName; |
||
830 | $this->_media->mime_type = $this->_media->file->type; |
||
831 | $this->handleFileUpload($this->_media->file); |
||
832 | |||
833 | if ($this->_media->save(false)) { |
||
834 | if ($this->_media->setMeta('metadata', $this->_meta)) { |
||
835 | $response = $this->generateResponse($this->_media); |
||
836 | } |
||
837 | } |
||
838 | } else { |
||
839 | $response = [ |
||
840 | 'error' => $this->_media->getFirstError('file'), |
||
841 | 'filename' => isset($this->_media->file->name) ? $this->_media->file->name : null, |
||
842 | 'file_size' => isset($this->_media->file->size) ? $this->_media->file->size : null, |
||
843 | ]; |
||
844 | } |
||
845 | |||
846 | $this->setResponse([ |
||
847 | $this->getOption('param_name') => [$response], |
||
848 | ]); |
||
849 | |||
850 | return $this->getResponse($printResponse); |
||
851 | } |
||
852 | |||
853 | /** |
||
854 | * Delete files based on media primary key |
||
855 | * |
||
856 | * @param int $id Primary key of Media |
||
857 | * @param bool $printResponse |
||
858 | * @return array |
||
859 | * @throws \Exception |
||
860 | */ |
||
861 | public function delete($id, $printResponse = self::PRINT_RESPONSE) |
||
862 | { |
||
863 | $success = true; |
||
864 | $response = []; |
||
865 | $media = $this->findMedia($id); |
||
866 | $metadata = $media->getMeta('metadata'); |
||
0 ignored issues
–
show
|
|||
867 | |||
868 | if ($media->delete()) { |
||
0 ignored issues
–
show
|
|||
869 | foreach ($metadata['versions'] as $version) { |
||
870 | $filePath = $this->getFilePath($version['url']); |
||
871 | $success = is_file($filePath) && unlink($filePath); |
||
872 | } |
||
873 | $response[$metadata['filename']] = $success; |
||
874 | } |
||
875 | |||
876 | $this->setResponse($response); |
||
877 | |||
878 | return $this->getResponse($printResponse); |
||
879 | } |
||
880 | } |
||
881 |
This check looks at variables that have been passed in as parameters and are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.