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 | * Resize and crop images on the fly, store generated images in a cache. |
||
4 | * |
||
5 | * @author Mikael Roos [email protected] |
||
6 | * @example http://dbwebb.se/opensource/cimage |
||
7 | * @link https://github.com/mosbth/cimage |
||
8 | */ |
||
9 | class CImage |
||
10 | { |
||
11 | |||
12 | /** |
||
13 | * Constants type of PNG image |
||
14 | */ |
||
15 | const PNG_GREYSCALE = 0; |
||
16 | const PNG_RGB = 2; |
||
17 | const PNG_RGB_PALETTE = 3; |
||
18 | const PNG_GREYSCALE_ALPHA = 4; |
||
19 | const PNG_RGB_ALPHA = 6; |
||
20 | |||
21 | |||
22 | |||
23 | /** |
||
24 | * Constant for default image quality when not set |
||
25 | */ |
||
26 | const JPEG_QUALITY_DEFAULT = 60; |
||
27 | |||
28 | |||
29 | |||
30 | /** |
||
31 | * Quality level for JPEG images. |
||
32 | */ |
||
33 | private $quality; |
||
34 | |||
35 | |||
36 | |||
37 | /** |
||
38 | * Is the quality level set from external use (true) or is it default (false)? |
||
39 | */ |
||
40 | private $useQuality = false; |
||
41 | |||
42 | |||
43 | |||
44 | /** |
||
45 | * Constant for default image quality when not set |
||
46 | */ |
||
47 | const PNG_COMPRESSION_DEFAULT = -1; |
||
48 | |||
49 | |||
50 | |||
51 | /** |
||
52 | * Compression level for PNG images. |
||
53 | */ |
||
54 | private $compress; |
||
55 | |||
56 | |||
57 | |||
58 | /** |
||
59 | * Is the compress level set from external use (true) or is it default (false)? |
||
60 | */ |
||
61 | private $useCompress = false; |
||
62 | |||
63 | |||
64 | |||
65 | |||
66 | /** |
||
67 | * Add HTTP headers for outputing image. |
||
68 | */ |
||
69 | private $HTTPHeader = array(); |
||
70 | |||
71 | |||
72 | |||
73 | /** |
||
74 | * Default background color, red, green, blue, alpha. |
||
75 | * |
||
76 | * @todo remake when upgrading to PHP 5.5 |
||
77 | */ |
||
78 | /* |
||
79 | const BACKGROUND_COLOR = array( |
||
80 | 'red' => 0, |
||
81 | 'green' => 0, |
||
82 | 'blue' => 0, |
||
83 | 'alpha' => null, |
||
84 | );*/ |
||
85 | |||
86 | |||
87 | |||
88 | /** |
||
89 | * Default background color to use. |
||
90 | * |
||
91 | * @todo remake when upgrading to PHP 5.5 |
||
92 | */ |
||
93 | //private $bgColorDefault = self::BACKGROUND_COLOR; |
||
94 | private $bgColorDefault = array( |
||
95 | 'red' => 0, |
||
96 | 'green' => 0, |
||
97 | 'blue' => 0, |
||
98 | 'alpha' => null, |
||
99 | ); |
||
100 | |||
101 | |||
102 | /** |
||
103 | * Background color to use, specified as part of options. |
||
104 | */ |
||
105 | private $bgColor; |
||
106 | |||
107 | |||
108 | |||
109 | /** |
||
110 | * Where to save the target file. |
||
111 | */ |
||
112 | private $saveFolder; |
||
113 | |||
114 | |||
115 | |||
116 | /** |
||
117 | * The working image object. |
||
118 | */ |
||
119 | private $image; |
||
120 | |||
121 | |||
122 | |||
123 | /** |
||
124 | * Image filename, may include subdirectory, relative from $imageFolder |
||
125 | */ |
||
126 | private $imageSrc; |
||
127 | |||
128 | |||
129 | |||
130 | /** |
||
131 | * Actual path to the image, $imageFolder . '/' . $imageSrc |
||
132 | */ |
||
133 | private $pathToImage; |
||
134 | |||
135 | |||
136 | |||
137 | /** |
||
138 | * File type for source image, as provided by getimagesize() |
||
139 | */ |
||
140 | private $fileType; |
||
141 | |||
142 | |||
143 | |||
144 | /** |
||
145 | * File extension to use when saving image. |
||
146 | */ |
||
147 | private $extension; |
||
148 | |||
149 | |||
150 | |||
151 | /** |
||
152 | * Output format, supports null (image) or json. |
||
153 | */ |
||
154 | private $outputFormat = null; |
||
155 | |||
156 | |||
157 | |||
158 | /** |
||
159 | * Do lossy output using external postprocessing tools. |
||
160 | */ |
||
161 | private $lossy = null; |
||
162 | |||
163 | |||
164 | |||
165 | /** |
||
166 | * Verbose mode to print out a trace and display the created image |
||
167 | */ |
||
168 | private $verbose = false; |
||
169 | |||
170 | |||
171 | |||
172 | /** |
||
173 | * Keep a log/trace on what happens |
||
174 | */ |
||
175 | private $log = array(); |
||
176 | |||
177 | |||
178 | |||
179 | /** |
||
180 | * Handle image as palette image |
||
181 | */ |
||
182 | private $palette; |
||
183 | |||
184 | |||
185 | |||
186 | /** |
||
187 | * Target filename, with path, to save resulting image in. |
||
188 | */ |
||
189 | private $cacheFileName; |
||
190 | |||
191 | |||
192 | |||
193 | /** |
||
194 | * Set a format to save image as, or null to use original format. |
||
195 | */ |
||
196 | private $saveAs; |
||
197 | |||
198 | |||
199 | /** |
||
200 | * Path to command for lossy optimize, for example pngquant. |
||
201 | */ |
||
202 | private $pngLossy; |
||
203 | private $pngLossyCmd; |
||
204 | |||
205 | |||
206 | |||
207 | /** |
||
208 | * Path to command for filter optimize, for example optipng. |
||
209 | */ |
||
210 | private $pngFilter; |
||
211 | private $pngFilterCmd; |
||
212 | |||
213 | |||
214 | |||
215 | /** |
||
216 | * Path to command for deflate optimize, for example pngout. |
||
217 | */ |
||
218 | private $pngDeflate; |
||
219 | private $pngDeflateCmd; |
||
220 | |||
221 | |||
222 | |||
223 | /** |
||
224 | * Path to command to optimize jpeg images, for example jpegtran or null. |
||
225 | */ |
||
226 | private $jpegOptimize; |
||
227 | private $jpegOptimizeCmd; |
||
228 | |||
229 | |||
230 | |||
231 | /** |
||
232 | * Image dimensions, calculated from loaded image. |
||
233 | */ |
||
234 | private $width; // Calculated from source image |
||
235 | private $height; // Calculated from source image |
||
236 | |||
237 | |||
238 | /** |
||
239 | * New image dimensions, incoming as argument or calculated. |
||
240 | */ |
||
241 | private $newWidth; |
||
242 | private $newWidthOrig; // Save original value |
||
243 | private $newHeight; |
||
244 | private $newHeightOrig; // Save original value |
||
245 | |||
246 | |||
247 | /** |
||
248 | * Change target height & width when different dpr, dpr 2 means double image dimensions. |
||
249 | */ |
||
250 | private $dpr = 1; |
||
251 | |||
252 | |||
253 | /** |
||
254 | * Always upscale images, even if they are smaller than target image. |
||
255 | */ |
||
256 | const UPSCALE_DEFAULT = true; |
||
257 | private $upscale = self::UPSCALE_DEFAULT; |
||
258 | |||
259 | |||
260 | |||
261 | /** |
||
262 | * Array with details on how to crop, incoming as argument and calculated. |
||
263 | */ |
||
264 | public $crop; |
||
265 | public $cropOrig; // Save original value |
||
266 | |||
267 | |||
268 | /** |
||
269 | * String with details on how to do image convolution. String |
||
270 | * should map a key in the $convolvs array or be a string of |
||
271 | * 11 float values separated by comma. The first nine builds |
||
272 | * up the matrix, then divisor and last offset. |
||
273 | */ |
||
274 | private $convolve; |
||
275 | |||
276 | |||
277 | /** |
||
278 | * Custom convolution expressions, matrix 3x3, divisor and offset. |
||
279 | */ |
||
280 | private $convolves = array( |
||
281 | 'lighten' => '0,0,0, 0,12,0, 0,0,0, 9, 0', |
||
282 | 'darken' => '0,0,0, 0,6,0, 0,0,0, 9, 0', |
||
283 | 'sharpen' => '-1,-1,-1, -1,16,-1, -1,-1,-1, 8, 0', |
||
284 | 'sharpen-alt' => '0,-1,0, -1,5,-1, 0,-1,0, 1, 0', |
||
285 | 'emboss' => '1,1,-1, 1,3,-1, 1,-1,-1, 3, 0', |
||
286 | 'emboss-alt' => '-2,-1,0, -1,1,1, 0,1,2, 1, 0', |
||
287 | 'blur' => '1,1,1, 1,15,1, 1,1,1, 23, 0', |
||
288 | 'gblur' => '1,2,1, 2,4,2, 1,2,1, 16, 0', |
||
289 | 'edge' => '-1,-1,-1, -1,8,-1, -1,-1,-1, 9, 0', |
||
290 | 'edge-alt' => '0,1,0, 1,-4,1, 0,1,0, 1, 0', |
||
291 | 'draw' => '0,-1,0, -1,5,-1, 0,-1,0, 0, 0', |
||
292 | 'mean' => '1,1,1, 1,1,1, 1,1,1, 9, 0', |
||
293 | 'motion' => '1,0,0, 0,1,0, 0,0,1, 3, 0', |
||
294 | ); |
||
295 | |||
296 | |||
297 | /** |
||
298 | * Resize strategy to fill extra area with background color. |
||
299 | * True or false. |
||
300 | */ |
||
301 | private $fillToFit; |
||
302 | |||
303 | |||
304 | |||
305 | /** |
||
306 | * To store value for option scale. |
||
307 | */ |
||
308 | private $scale; |
||
309 | |||
310 | |||
311 | |||
312 | /** |
||
313 | * To store value for option. |
||
314 | */ |
||
315 | private $rotateBefore; |
||
316 | |||
317 | |||
318 | |||
319 | /** |
||
320 | * To store value for option. |
||
321 | */ |
||
322 | private $rotateAfter; |
||
323 | |||
324 | |||
325 | |||
326 | /** |
||
327 | * To store value for option. |
||
328 | */ |
||
329 | private $autoRotate; |
||
330 | |||
331 | |||
332 | |||
333 | /** |
||
334 | * To store value for option. |
||
335 | */ |
||
336 | private $sharpen; |
||
337 | |||
338 | |||
339 | |||
340 | /** |
||
341 | * To store value for option. |
||
342 | */ |
||
343 | private $emboss; |
||
344 | |||
345 | |||
346 | |||
347 | /** |
||
348 | * To store value for option. |
||
349 | */ |
||
350 | private $blur; |
||
351 | |||
352 | |||
353 | |||
354 | /** |
||
355 | * Used with option area to set which parts of the image to use. |
||
356 | */ |
||
357 | private $offset; |
||
358 | |||
359 | |||
360 | |||
361 | /** |
||
362 | * Calculate target dimension for image when using fill-to-fit resize strategy. |
||
363 | */ |
||
364 | private $fillWidth; |
||
365 | private $fillHeight; |
||
366 | |||
367 | |||
368 | |||
369 | /** |
||
370 | * Allow remote file download, default is to disallow remote file download. |
||
371 | */ |
||
372 | private $allowRemote = false; |
||
373 | |||
374 | |||
375 | |||
376 | /** |
||
377 | * Path to cache for remote download. |
||
378 | */ |
||
379 | private $remoteCache; |
||
380 | |||
381 | |||
382 | |||
383 | /** |
||
384 | * Pattern to recognize a remote file. |
||
385 | */ |
||
386 | //private $remotePattern = '#^[http|https]://#'; |
||
387 | private $remotePattern = '#^https?://#'; |
||
388 | |||
389 | |||
390 | |||
391 | /** |
||
392 | * Use the cache if true, set to false to ignore the cached file. |
||
393 | */ |
||
394 | private $useCache = true; |
||
395 | |||
396 | |||
397 | /** |
||
398 | * Disable the fasttrackCacke to start with, inject an object to enable it. |
||
399 | */ |
||
400 | private $fastTrackCache = null; |
||
401 | |||
402 | |||
403 | |||
404 | /* |
||
405 | * Set whitelist for valid hostnames from where remote source can be |
||
406 | * downloaded. |
||
407 | */ |
||
408 | private $remoteHostWhitelist = null; |
||
409 | |||
410 | |||
411 | |||
412 | /* |
||
413 | * Do verbose logging to file by setting this to a filename. |
||
414 | */ |
||
415 | private $verboseFileName = null; |
||
416 | |||
417 | |||
418 | |||
419 | /* |
||
420 | * Output to ascii can take som options as an array. |
||
421 | */ |
||
422 | private $asciiOptions = array(); |
||
423 | |||
424 | |||
425 | |||
426 | /* |
||
427 | * Use interlaced progressive mode for JPEG images. |
||
428 | */ |
||
429 | private $interlace = false; |
||
430 | |||
431 | |||
432 | |||
433 | /* |
||
434 | 7 | * Image copy strategy, defaults to RESAMPLE. |
|
435 | */ |
||
436 | 7 | const RESIZE = 1; |
|
437 | 7 | const RESAMPLE = 2; |
|
438 | 7 | private $copyStrategy = NULL; |
|
439 | |||
440 | |||
441 | |||
442 | /** |
||
443 | * Properties, the class is mutable and the method setOptions() |
||
444 | * decides (partly) what properties are created. |
||
445 | * |
||
446 | * @todo Clean up these and check if and how they are used |
||
447 | */ |
||
448 | |||
449 | public $keepRatio; |
||
450 | public $cropToFit; |
||
451 | private $cropWidth; |
||
452 | private $cropHeight; |
||
453 | public $crop_x; |
||
454 | public $crop_y; |
||
455 | public $filters; |
||
456 | private $attr; // Calculated from source image |
||
457 | |||
458 | |||
459 | |||
460 | |||
461 | /** |
||
462 | * Constructor, can take arguments to init the object. |
||
463 | * |
||
464 | * @param string $imageSrc filename which may contain subdirectory. |
||
465 | * @param string $imageFolder path to root folder for images. |
||
466 | * @param string $saveFolder path to folder where to save the new file or null to skip saving. |
||
467 | 2 | * @param string $saveName name of target file when saveing. |
|
468 | */ |
||
469 | 2 | public function __construct($imageSrc = null, $imageFolder = null, $saveFolder = null, $saveName = null) |
|
470 | 2 | { |
|
471 | $this->setSource($imageSrc, $imageFolder); |
||
472 | $this->setTarget($saveFolder, $saveName); |
||
473 | } |
||
474 | |||
475 | |||
476 | |||
477 | /** |
||
478 | * Inject object and use it, must be available as member. |
||
479 | * |
||
480 | * @param string $property to set as object. |
||
481 | * @param object $object to set to property. |
||
482 | * |
||
483 | * @return $this |
||
484 | */ |
||
485 | public function injectDependency($property, $object) |
||
486 | { |
||
487 | if (!property_exists($this, $property)) { |
||
488 | $this->raiseError("Injecting unknown property."); |
||
489 | } |
||
490 | $this->$property = $object; |
||
491 | return $this; |
||
492 | } |
||
493 | |||
494 | |||
495 | |||
496 | /** |
||
497 | * Set verbose mode. |
||
498 | * |
||
499 | 2 | * @param boolean $mode true or false to enable and disable verbose mode, |
|
500 | * default is true. |
||
501 | 2 | * |
|
502 | 2 | * @return $this |
|
503 | */ |
||
504 | 2 | public function setVerbose($mode = true) |
|
505 | { |
||
506 | 2 | $this->verbose = $mode; |
|
507 | return $this; |
||
508 | } |
||
509 | |||
510 | |||
511 | |||
512 | /** |
||
513 | * Set save folder, base folder for saving cache files. |
||
514 | * |
||
515 | * @todo clean up how $this->saveFolder is used in other methods. |
||
516 | * |
||
517 | * @param string $path where to store cached files. |
||
518 | * |
||
519 | 2 | * @return $this |
|
520 | */ |
||
521 | 2 | public function setSaveFolder($path) |
|
522 | 2 | { |
|
523 | $this->saveFolder = $path; |
||
524 | 2 | return $this; |
|
525 | } |
||
526 | 2 | ||
527 | 2 | ||
528 | 2 | ||
529 | 2 | /** |
|
530 | * Use cache or not. |
||
531 | 2 | * |
|
532 | * @param boolean $use true or false to use cache. |
||
533 | * |
||
534 | * @return $this |
||
535 | */ |
||
536 | public function useCache($use = true) |
||
537 | { |
||
538 | $this->useCache = $use; |
||
539 | return $this; |
||
540 | } |
||
541 | |||
542 | |||
543 | 2 | ||
544 | /** |
||
545 | 2 | * Create and save a dummy image. Use dimensions as stated in |
|
546 | 2 | * $this->newWidth, or $width or default to 100 (same for height. |
|
547 | 2 | * |
|
548 | * @param integer $width use specified width for image dimension. |
||
549 | * @param integer $height use specified width for image dimension. |
||
550 | * |
||
551 | * @return $this |
||
552 | */ |
||
553 | public function createDummyImage($width = null, $height = null) |
||
554 | { |
||
555 | $this->newWidth = $this->newWidth ?: $width ?: 100; |
||
556 | $this->newHeight = $this->newHeight ?: $height ?: 100; |
||
557 | |||
558 | $this->image = $this->CreateImageKeepTransparency($this->newWidth, $this->newHeight); |
||
559 | |||
560 | 2 | return $this; |
|
561 | } |
||
562 | 2 | ||
563 | 2 | ||
564 | |||
565 | 2 | /** |
|
566 | 2 | * Allow or disallow remote image download. |
|
567 | 2 | * |
|
568 | * @param boolean $allow true or false to enable and disable. |
||
569 | * @param string $cache path to cache dir. |
||
570 | * @param string $pattern to use to detect if its a remote file. |
||
571 | * |
||
572 | * @return $this |
||
573 | */ |
||
574 | public function setRemoteDownload($allow, $cache, $pattern = null) |
||
575 | { |
||
576 | $this->allowRemote = $allow; |
||
577 | $this->remoteCache = $cache; |
||
578 | $this->remotePattern = is_null($pattern) ? $this->remotePattern : $pattern; |
||
579 | |||
580 | 3 | $this->log( |
|
581 | "Set remote download to: " |
||
582 | 3 | . ($this->allowRemote ? "true" : "false") |
|
583 | 1 | . " using pattern " |
|
584 | 1 | . $this->remotePattern |
|
585 | ); |
||
586 | |||
587 | 2 | return $this; |
|
588 | 2 | } |
|
589 | 2 | ||
590 | |||
591 | 2 | ||
592 | /** |
||
593 | 2 | * Check if the image resource is a remote file or not. |
|
594 | 2 | * |
|
595 | 2 | * @param string $src check if src is remote. |
|
596 | * |
||
597 | * @return boolean true if $src is a remote file, else false. |
||
598 | */ |
||
599 | public function isRemoteSource($src) |
||
600 | { |
||
601 | $remote = preg_match($this->remotePattern, $src); |
||
602 | $this->log("Detected remote image: " . ($remote ? "true" : "false")); |
||
603 | return !!$remote; |
||
604 | } |
||
605 | |||
606 | |||
607 | |||
608 | /** |
||
609 | * Set whitelist for valid hostnames from where remote source can be |
||
610 | * downloaded. |
||
611 | * |
||
612 | * @param array $whitelist with regexp hostnames to allow download from. |
||
613 | * |
||
614 | * @return $this |
||
615 | */ |
||
616 | public function setRemoteHostWhitelist($whitelist = null) |
||
617 | { |
||
618 | $this->remoteHostWhitelist = $whitelist; |
||
619 | $this->log( |
||
620 | "Setting remote host whitelist to: " |
||
621 | . (is_null($whitelist) ? "null" : print_r($whitelist, 1)) |
||
622 | ); |
||
623 | return $this; |
||
624 | } |
||
625 | |||
626 | 2 | ||
627 | |||
628 | 2 | /** |
|
629 | * Check if the hostname for the remote image, is on a whitelist, |
||
630 | 2 | * if the whitelist is defined. |
|
631 | * |
||
632 | * @param string $src the remote source. |
||
633 | * |
||
634 | 2 | * @return boolean true if hostname on $src is in the whitelist, else false. |
|
635 | */ |
||
636 | public function isRemoteSourceOnWhitelist($src) |
||
637 | { |
||
638 | if (is_null($this->remoteHostWhitelist)) { |
||
639 | $this->log("Remote host on whitelist not configured - allowing."); |
||
640 | return true; |
||
641 | } |
||
642 | |||
643 | $whitelist = new CWhitelist(); |
||
644 | $hostname = parse_url($src, PHP_URL_HOST); |
||
645 | $allow = $whitelist->check($hostname, $this->remoteHostWhitelist); |
||
0 ignored issues
–
show
|
|||
646 | |||
647 | $this->log( |
||
648 | "Remote host is on whitelist: " |
||
649 | . ($allow ? "true" : "false") |
||
650 | ); |
||
651 | return $allow; |
||
652 | } |
||
653 | |||
654 | |||
655 | |||
656 | /** |
||
657 | * Check if file extension is valid as a file extension. |
||
658 | * |
||
659 | * @param string $extension of image file. |
||
660 | * |
||
661 | * @return $this |
||
662 | */ |
||
663 | private function checkFileExtension($extension) |
||
664 | { |
||
665 | $valid = array('jpg', 'jpeg', 'png', 'gif', 'webp'); |
||
666 | |||
667 | in_array(strtolower($extension), $valid) |
||
668 | or $this->raiseError('Not a valid file extension.'); |
||
669 | |||
670 | return $this; |
||
671 | } |
||
672 | |||
673 | |||
674 | |||
675 | /** |
||
676 | * Normalize the file extension. |
||
677 | * |
||
678 | * @param string $extension of image file or skip to use internal. |
||
679 | * |
||
680 | * @return string $extension as a normalized file extension. |
||
681 | */ |
||
682 | private function normalizeFileExtension($extension = null) |
||
683 | { |
||
684 | $extension = strtolower($extension ? $extension : $this->extension); |
||
685 | |||
686 | if ($extension == 'jpeg') { |
||
687 | $extension = 'jpg'; |
||
688 | 7 | } |
|
689 | |||
690 | 7 | return $extension; |
|
691 | 7 | } |
|
692 | 7 | ||
693 | 7 | ||
694 | |||
695 | /** |
||
696 | 2 | * Download a remote image and return path to its local copy. |
|
697 | * |
||
698 | * @param string $src remote path to image. |
||
699 | * |
||
700 | * @return string as path to downloaded remote source. |
||
701 | 2 | */ |
|
702 | public function downloadRemoteSource($src) |
||
703 | { |
||
704 | if (!$this->isRemoteSourceOnWhitelist($src)) { |
||
705 | throw new Exception("Hostname is not on whitelist for remote sources."); |
||
706 | 2 | } |
|
707 | 2 | ||
708 | 2 | $remote = new CRemoteImage(); |
|
709 | |||
710 | 2 | if (!is_writable($this->remoteCache)) { |
|
711 | $this->log("The remote cache is not writable."); |
||
712 | } |
||
713 | |||
714 | $remote->setCache($this->remoteCache); |
||
715 | $remote->useCache($this->useCache); |
||
716 | $src = $remote->download($src); |
||
717 | |||
718 | $this->log("Remote HTTP status: " . $remote->getStatus()); |
||
719 | $this->log("Remote item is in local cache: $src"); |
||
720 | $this->log("Remote details on cache:" . print_r($remote->getDetails(), true)); |
||
721 | |||
722 | return $src; |
||
723 | } |
||
724 | 7 | ||
725 | |||
726 | 7 | ||
727 | 7 | /** |
|
728 | 7 | * Set source file to use as image source. |
|
729 | * |
||
730 | * @param string $src of image. |
||
731 | 2 | * @param string $dir as optional base directory where images are. |
|
732 | * |
||
733 | * @return $this |
||
734 | */ |
||
735 | 2 | public function setSource($src, $dir = null) |
|
736 | { |
||
737 | if (!isset($src)) { |
||
738 | 2 | $this->imageSrc = null; |
|
739 | 2 | $this->pathToImage = null; |
|
740 | return $this; |
||
741 | 2 | } |
|
742 | |||
743 | if ($this->allowRemote && $this->isRemoteSource($src)) { |
||
744 | $src = $this->downloadRemoteSource($src); |
||
745 | $dir = null; |
||
746 | } |
||
747 | |||
748 | if (!isset($dir)) { |
||
749 | $dir = dirname($src); |
||
750 | $src = basename($src); |
||
751 | 2 | } |
|
752 | |||
753 | 2 | $this->imageSrc = ltrim($src, '/'); |
|
754 | $imageFolder = rtrim($dir, '/'); |
||
755 | $this->pathToImage = $imageFolder . '/' . $this->imageSrc; |
||
756 | |||
757 | return $this; |
||
758 | } |
||
759 | |||
760 | |||
761 | |||
762 | /** |
||
763 | * Set target file. |
||
764 | * |
||
765 | * @param string $src of target image. |
||
766 | * @param string $dir as optional base directory where images are stored. |
||
767 | * Uses $this->saveFolder if null. |
||
768 | * |
||
769 | * @return $this |
||
770 | */ |
||
771 | public function setTarget($src = null, $dir = null) |
||
772 | { |
||
773 | if (!isset($src)) { |
||
774 | $this->cacheFileName = null; |
||
775 | return $this; |
||
776 | } |
||
777 | |||
778 | if (isset($dir)) { |
||
779 | $this->saveFolder = rtrim($dir, '/'); |
||
780 | } |
||
781 | |||
782 | $this->cacheFileName = $this->saveFolder . '/' . $src; |
||
783 | |||
784 | // Sanitize filename |
||
785 | $this->cacheFileName = preg_replace('/^a-zA-Z0-9\.-_/', '', $this->cacheFileName); |
||
786 | $this->log("The cache file name is: " . $this->cacheFileName); |
||
787 | |||
788 | return $this; |
||
789 | } |
||
790 | |||
791 | |||
792 | |||
793 | /** |
||
794 | * Get filename of target file. |
||
795 | * |
||
796 | * @return Boolean|String as filename of target or false if not set. |
||
797 | */ |
||
798 | public function getTarget() |
||
799 | { |
||
800 | return $this->cacheFileName; |
||
801 | } |
||
802 | |||
803 | |||
804 | |||
805 | /** |
||
806 | * Set options to use when processing image. |
||
807 | * |
||
808 | * @param array $args used when processing image. |
||
809 | * |
||
810 | * @return $this |
||
811 | */ |
||
812 | public function setOptions($args) |
||
813 | { |
||
814 | $this->log("Set new options for processing image."); |
||
815 | |||
816 | $defaults = array( |
||
817 | // Options for calculate dimensions |
||
818 | 'newWidth' => null, |
||
819 | 'newHeight' => null, |
||
820 | 'aspectRatio' => null, |
||
821 | 'keepRatio' => true, |
||
822 | 'cropToFit' => false, |
||
823 | 'fillToFit' => null, |
||
824 | 'crop' => null, //array('width'=>null, 'height'=>null, 'start_x'=>0, 'start_y'=>0), |
||
825 | 'area' => null, //'0,0,0,0', |
||
826 | 'upscale' => self::UPSCALE_DEFAULT, |
||
827 | |||
828 | // Options for caching or using original |
||
829 | 'useCache' => true, |
||
830 | 'useOriginal' => true, |
||
831 | |||
832 | // Pre-processing, before resizing is done |
||
833 | 'scale' => null, |
||
834 | 'rotateBefore' => null, |
||
835 | 'autoRotate' => false, |
||
836 | |||
837 | // General options |
||
838 | 'bgColor' => null, |
||
839 | |||
840 | // Post-processing, after resizing is done |
||
841 | 'palette' => null, |
||
842 | 'filters' => null, |
||
843 | 'sharpen' => null, |
||
844 | 'emboss' => null, |
||
845 | 'blur' => null, |
||
846 | 'convolve' => null, |
||
847 | 'rotateAfter' => null, |
||
848 | 'interlace' => null, |
||
849 | |||
850 | // Output format |
||
851 | 'outputFormat' => null, |
||
852 | 'dpr' => 1, |
||
853 | |||
854 | // Postprocessing using external tools |
||
855 | 'lossy' => null, |
||
856 | ); |
||
857 | |||
858 | // Convert crop settings from string to array |
||
859 | if (isset($args['crop']) && !is_array($args['crop'])) { |
||
860 | $pices = explode(',', $args['crop']); |
||
861 | $args['crop'] = array( |
||
862 | 'width' => $pices[0], |
||
863 | 'height' => $pices[1], |
||
864 | 'start_x' => $pices[2], |
||
865 | 'start_y' => $pices[3], |
||
866 | ); |
||
867 | } |
||
868 | |||
869 | // Convert area settings from string to array |
||
870 | if (isset($args['area']) && !is_array($args['area'])) { |
||
871 | $pices = explode(',', $args['area']); |
||
872 | $args['area'] = array( |
||
873 | 'top' => $pices[0], |
||
874 | 'right' => $pices[1], |
||
875 | 'bottom' => $pices[2], |
||
876 | 'left' => $pices[3], |
||
877 | ); |
||
878 | } |
||
879 | |||
880 | // Convert filter settings from array of string to array of array |
||
881 | if (isset($args['filters']) && is_array($args['filters'])) { |
||
882 | foreach ($args['filters'] as $key => $filterStr) { |
||
883 | $parts = explode(',', $filterStr); |
||
884 | $filter = $this->mapFilter($parts[0]); |
||
885 | $filter['str'] = $filterStr; |
||
886 | for ($i=1; $i<=$filter['argc']; $i++) { |
||
887 | if (isset($parts[$i])) { |
||
888 | $filter["arg{$i}"] = $parts[$i]; |
||
889 | } else { |
||
890 | throw new Exception( |
||
891 | 'Missing arg to filter, review how many arguments are needed at |
||
892 | http://php.net/manual/en/function.imagefilter.php' |
||
893 | ); |
||
894 | } |
||
895 | } |
||
896 | $args['filters'][$key] = $filter; |
||
897 | } |
||
898 | } |
||
899 | |||
900 | // Merge default arguments with incoming and set properties. |
||
901 | //$args = array_merge_recursive($defaults, $args); |
||
902 | $args = array_merge($defaults, $args); |
||
903 | foreach ($defaults as $key => $val) { |
||
904 | $this->{$key} = $args[$key]; |
||
905 | } |
||
906 | |||
907 | if ($this->bgColor) { |
||
908 | $this->setDefaultBackgroundColor($this->bgColor); |
||
909 | } |
||
910 | |||
911 | // Save original values to enable re-calculating |
||
912 | $this->newWidthOrig = $this->newWidth; |
||
913 | $this->newHeightOrig = $this->newHeight; |
||
914 | $this->cropOrig = $this->crop; |
||
915 | |||
916 | return $this; |
||
917 | } |
||
918 | |||
919 | |||
920 | |||
921 | /** |
||
922 | * Map filter name to PHP filter and id. |
||
923 | * |
||
924 | * @param string $name the name of the filter. |
||
925 | * |
||
926 | * @return array with filter settings |
||
927 | * @throws Exception |
||
928 | */ |
||
929 | private function mapFilter($name) |
||
930 | { |
||
931 | $map = array( |
||
932 | 'negate' => array('id'=>0, 'argc'=>0, 'type'=>IMG_FILTER_NEGATE), |
||
933 | 'grayscale' => array('id'=>1, 'argc'=>0, 'type'=>IMG_FILTER_GRAYSCALE), |
||
934 | 'brightness' => array('id'=>2, 'argc'=>1, 'type'=>IMG_FILTER_BRIGHTNESS), |
||
935 | 'contrast' => array('id'=>3, 'argc'=>1, 'type'=>IMG_FILTER_CONTRAST), |
||
936 | 'colorize' => array('id'=>4, 'argc'=>4, 'type'=>IMG_FILTER_COLORIZE), |
||
937 | 'edgedetect' => array('id'=>5, 'argc'=>0, 'type'=>IMG_FILTER_EDGEDETECT), |
||
938 | 'emboss' => array('id'=>6, 'argc'=>0, 'type'=>IMG_FILTER_EMBOSS), |
||
939 | 'gaussian_blur' => array('id'=>7, 'argc'=>0, 'type'=>IMG_FILTER_GAUSSIAN_BLUR), |
||
940 | 'selective_blur' => array('id'=>8, 'argc'=>0, 'type'=>IMG_FILTER_SELECTIVE_BLUR), |
||
941 | 'mean_removal' => array('id'=>9, 'argc'=>0, 'type'=>IMG_FILTER_MEAN_REMOVAL), |
||
942 | 'smooth' => array('id'=>10, 'argc'=>1, 'type'=>IMG_FILTER_SMOOTH), |
||
943 | 'pixelate' => array('id'=>11, 'argc'=>2, 'type'=>IMG_FILTER_PIXELATE), |
||
944 | ); |
||
945 | |||
946 | if (isset($map[$name])) { |
||
947 | return $map[$name]; |
||
948 | } else { |
||
949 | throw new Exception('No such filter.'); |
||
950 | } |
||
951 | } |
||
952 | |||
953 | |||
954 | |||
955 | /** |
||
956 | * Load image details from original image file. |
||
957 | * |
||
958 | * @param string $file the file to load or null to use $this->pathToImage. |
||
959 | * |
||
960 | * @return $this |
||
961 | * @throws Exception |
||
962 | */ |
||
963 | public function loadImageDetails($file = null) |
||
964 | { |
||
965 | $file = $file ? $file : $this->pathToImage; |
||
966 | |||
967 | is_readable($file) |
||
968 | or $this->raiseError('Image file does not exist.'); |
||
969 | |||
970 | $info = list($this->width, $this->height, $this->fileType) = getimagesize($file); |
||
971 | if (empty($info)) { |
||
972 | // To support webp |
||
973 | $this->fileType = false; |
||
974 | if (function_exists("exif_imagetype")) { |
||
975 | $this->fileType = exif_imagetype($file); |
||
976 | if ($this->fileType === false) { |
||
977 | if (function_exists("imagecreatefromwebp")) { |
||
978 | $webp = imagecreatefromwebp($file); |
||
979 | if ($webp !== false) { |
||
980 | $this->width = imagesx($webp); |
||
981 | $this->height = imagesy($webp); |
||
982 | $this->fileType = IMG_WEBP; |
||
983 | } |
||
984 | } |
||
985 | } |
||
986 | } |
||
987 | } |
||
988 | |||
989 | if (!$this->fileType) { |
||
990 | throw new Exception("Loading image details, the file doesn't seem to be a valid image."); |
||
991 | } |
||
992 | |||
993 | if ($this->verbose) { |
||
994 | $this->log("Loading image details for: {$file}"); |
||
995 | $this->log(" Image width x height (type): {$this->width} x {$this->height} ({$this->fileType})."); |
||
996 | $this->log(" Image filesize: " . filesize($file) . " bytes."); |
||
997 | $this->log(" Image mimetype: " . $this->getMimeType()); |
||
998 | } |
||
999 | |||
1000 | return $this; |
||
1001 | } |
||
1002 | |||
1003 | |||
1004 | |||
1005 | /** |
||
1006 | * Get mime type for image type. |
||
1007 | * |
||
1008 | * @return $this |
||
1009 | * @throws Exception |
||
1010 | */ |
||
1011 | protected function getMimeType() |
||
1012 | { |
||
1013 | if ($this->fileType === IMG_WEBP) { |
||
1014 | return "image/webp"; |
||
1015 | } |
||
1016 | |||
1017 | return image_type_to_mime_type($this->fileType); |
||
1018 | } |
||
1019 | |||
1020 | |||
1021 | |||
1022 | /** |
||
1023 | * Init new width and height and do some sanity checks on constraints, before any |
||
1024 | * processing can be done. |
||
1025 | * |
||
1026 | * @return $this |
||
1027 | * @throws Exception |
||
1028 | */ |
||
1029 | public function initDimensions() |
||
1030 | { |
||
1031 | $this->log("Init dimension (before) newWidth x newHeight is {$this->newWidth} x {$this->newHeight}."); |
||
1032 | |||
1033 | // width as % |
||
1034 | if ($this->newWidth |
||
1035 | && $this->newWidth[strlen($this->newWidth)-1] == '%') { |
||
1036 | $this->newWidth = $this->width * substr($this->newWidth, 0, -1) / 100; |
||
1037 | $this->log("Setting new width based on % to {$this->newWidth}"); |
||
1038 | } |
||
1039 | |||
1040 | // height as % |
||
1041 | if ($this->newHeight |
||
1042 | && $this->newHeight[strlen($this->newHeight)-1] == '%') { |
||
1043 | $this->newHeight = $this->height * substr($this->newHeight, 0, -1) / 100; |
||
1044 | $this->log("Setting new height based on % to {$this->newHeight}"); |
||
1045 | } |
||
1046 | |||
1047 | is_null($this->aspectRatio) or is_numeric($this->aspectRatio) or $this->raiseError('Aspect ratio out of range'); |
||
0 ignored issues
–
show
The property
aspectRatio does not exist. Did you maybe forget to declare it?
In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code: class MyClass { }
$x = new MyClass();
$x->foo = true;
Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion: class MyClass {
public $foo;
}
$x = new MyClass();
$x->foo = true;
![]() |
|||
1048 | |||
1049 | // width & height from aspect ratio |
||
1050 | if ($this->aspectRatio && is_null($this->newWidth) && is_null($this->newHeight)) { |
||
1051 | if ($this->aspectRatio >= 1) { |
||
1052 | $this->newWidth = $this->width; |
||
1053 | $this->newHeight = $this->width / $this->aspectRatio; |
||
1054 | $this->log("Setting new width & height based on width & aspect ratio (>=1) to (w x h) {$this->newWidth} x {$this->newHeight}"); |
||
1055 | |||
1056 | } else { |
||
1057 | $this->newHeight = $this->height; |
||
1058 | $this->newWidth = $this->height * $this->aspectRatio; |
||
1059 | $this->log("Setting new width & height based on width & aspect ratio (<1) to (w x h) {$this->newWidth} x {$this->newHeight}"); |
||
1060 | } |
||
1061 | |||
1062 | } elseif ($this->aspectRatio && is_null($this->newWidth)) { |
||
1063 | $this->newWidth = $this->newHeight * $this->aspectRatio; |
||
1064 | $this->log("Setting new width based on aspect ratio to {$this->newWidth}"); |
||
1065 | |||
1066 | } elseif ($this->aspectRatio && is_null($this->newHeight)) { |
||
1067 | $this->newHeight = $this->newWidth / $this->aspectRatio; |
||
1068 | $this->log("Setting new height based on aspect ratio to {$this->newHeight}"); |
||
1069 | } |
||
1070 | |||
1071 | // Change width & height based on dpr |
||
1072 | if ($this->dpr != 1) { |
||
1073 | if (!is_null($this->newWidth)) { |
||
1074 | $this->newWidth = round($this->newWidth * $this->dpr); |
||
1075 | $this->log("Setting new width based on dpr={$this->dpr} - w={$this->newWidth}"); |
||
1076 | } |
||
1077 | if (!is_null($this->newHeight)) { |
||
1078 | $this->newHeight = round($this->newHeight * $this->dpr); |
||
1079 | $this->log("Setting new height based on dpr={$this->dpr} - h={$this->newHeight}"); |
||
1080 | } |
||
1081 | } |
||
1082 | |||
1083 | // Check values to be within domain |
||
1084 | is_null($this->newWidth) |
||
1085 | or is_numeric($this->newWidth) |
||
1086 | or $this->raiseError('Width not numeric'); |
||
1087 | |||
1088 | is_null($this->newHeight) |
||
1089 | or is_numeric($this->newHeight) |
||
1090 | or $this->raiseError('Height not numeric'); |
||
1091 | |||
1092 | $this->log("Init dimension (after) newWidth x newHeight is {$this->newWidth} x {$this->newHeight}."); |
||
1093 | |||
1094 | return $this; |
||
1095 | } |
||
1096 | |||
1097 | |||
1098 | |||
1099 | /** |
||
1100 | * Calculate new width and height of image, based on settings. |
||
1101 | * |
||
1102 | * @return $this |
||
1103 | */ |
||
1104 | public function calculateNewWidthAndHeight() |
||
1105 | { |
||
1106 | // Crop, use cropped width and height as base for calulations |
||
1107 | $this->log("Calculate new width and height."); |
||
1108 | $this->log("Original width x height is {$this->width} x {$this->height}."); |
||
1109 | $this->log("Target dimension (before calculating) newWidth x newHeight is {$this->newWidth} x {$this->newHeight}."); |
||
1110 | |||
1111 | // Check if there is an area to crop off |
||
1112 | if (isset($this->area)) { |
||
1113 | $this->offset['top'] = round($this->area['top'] / 100 * $this->height); |
||
0 ignored issues
–
show
The property
area does not exist. Did you maybe forget to declare it?
In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code: class MyClass { }
$x = new MyClass();
$x->foo = true;
Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion: class MyClass {
public $foo;
}
$x = new MyClass();
$x->foo = true;
![]() |
|||
1114 | $this->offset['right'] = round($this->area['right'] / 100 * $this->width); |
||
1115 | $this->offset['bottom'] = round($this->area['bottom'] / 100 * $this->height); |
||
1116 | $this->offset['left'] = round($this->area['left'] / 100 * $this->width); |
||
1117 | $this->offset['width'] = $this->width - $this->offset['left'] - $this->offset['right']; |
||
1118 | $this->offset['height'] = $this->height - $this->offset['top'] - $this->offset['bottom']; |
||
1119 | $this->width = $this->offset['width']; |
||
1120 | $this->height = $this->offset['height']; |
||
1121 | $this->log("The offset for the area to use is top {$this->area['top']}%, right {$this->area['right']}%, bottom {$this->area['bottom']}%, left {$this->area['left']}%."); |
||
1122 | $this->log("The offset for the area to use is top {$this->offset['top']}px, right {$this->offset['right']}px, bottom {$this->offset['bottom']}px, left {$this->offset['left']}px, width {$this->offset['width']}px, height {$this->offset['height']}px."); |
||
1123 | } |
||
1124 | |||
1125 | $width = $this->width; |
||
1126 | $height = $this->height; |
||
1127 | |||
1128 | // Check if crop is set |
||
1129 | if ($this->crop) { |
||
1130 | $width = $this->crop['width'] = $this->crop['width'] <= 0 ? $this->width + $this->crop['width'] : $this->crop['width']; |
||
1131 | $height = $this->crop['height'] = $this->crop['height'] <= 0 ? $this->height + $this->crop['height'] : $this->crop['height']; |
||
1132 | |||
1133 | if ($this->crop['start_x'] == 'left') { |
||
1134 | $this->crop['start_x'] = 0; |
||
1135 | } elseif ($this->crop['start_x'] == 'right') { |
||
1136 | $this->crop['start_x'] = $this->width - $width; |
||
1137 | } elseif ($this->crop['start_x'] == 'center') { |
||
1138 | $this->crop['start_x'] = round($this->width / 2) - round($width / 2); |
||
1139 | } |
||
1140 | |||
1141 | if ($this->crop['start_y'] == 'top') { |
||
1142 | $this->crop['start_y'] = 0; |
||
1143 | } elseif ($this->crop['start_y'] == 'bottom') { |
||
1144 | $this->crop['start_y'] = $this->height - $height; |
||
1145 | } elseif ($this->crop['start_y'] == 'center') { |
||
1146 | $this->crop['start_y'] = round($this->height / 2) - round($height / 2); |
||
1147 | } |
||
1148 | |||
1149 | $this->log("Crop area is width {$width}px, height {$height}px, start_x {$this->crop['start_x']}px, start_y {$this->crop['start_y']}px."); |
||
1150 | } |
||
1151 | |||
1152 | // Calculate new width and height if keeping aspect-ratio. |
||
1153 | if ($this->keepRatio) { |
||
1154 | |||
1155 | $this->log("Keep aspect ratio."); |
||
1156 | |||
1157 | // Crop-to-fit and both new width and height are set. |
||
1158 | if (($this->cropToFit || $this->fillToFit) && isset($this->newWidth) && isset($this->newHeight)) { |
||
1159 | |||
1160 | // Use newWidth and newHeigh as width/height, image should fit in box. |
||
1161 | $this->log("Use newWidth and newHeigh as width/height, image should fit in box."); |
||
1162 | |||
1163 | } elseif (isset($this->newWidth) && isset($this->newHeight)) { |
||
1164 | |||
1165 | // Both new width and height are set. |
||
1166 | // Use newWidth and newHeigh as max width/height, image should not be larger. |
||
1167 | $ratioWidth = $width / $this->newWidth; |
||
1168 | $ratioHeight = $height / $this->newHeight; |
||
1169 | $ratio = ($ratioWidth > $ratioHeight) ? $ratioWidth : $ratioHeight; |
||
1170 | $this->newWidth = round($width / $ratio); |
||
1171 | $this->newHeight = round($height / $ratio); |
||
1172 | $this->log("New width and height was set."); |
||
1173 | |||
1174 | } elseif (isset($this->newWidth)) { |
||
1175 | |||
1176 | // Use new width as max-width |
||
1177 | $factor = (float)$this->newWidth / (float)$width; |
||
1178 | $this->newHeight = round($factor * $height); |
||
1179 | $this->log("New width was set."); |
||
1180 | |||
1181 | } elseif (isset($this->newHeight)) { |
||
1182 | |||
1183 | // Use new height as max-hight |
||
1184 | $factor = (float)$this->newHeight / (float)$height; |
||
1185 | $this->newWidth = round($factor * $width); |
||
1186 | $this->log("New height was set."); |
||
1187 | |||
1188 | } else { |
||
1189 | |||
1190 | // Use existing width and height as new width and height. |
||
1191 | $this->newWidth = $width; |
||
1192 | $this->newHeight = $height; |
||
1193 | } |
||
1194 | |||
1195 | |||
1196 | // Get image dimensions for pre-resize image. |
||
1197 | if ($this->cropToFit || $this->fillToFit) { |
||
1198 | |||
1199 | // Get relations of original & target image |
||
1200 | $ratioWidth = $width / $this->newWidth; |
||
1201 | $ratioHeight = $height / $this->newHeight; |
||
1202 | |||
1203 | if ($this->cropToFit) { |
||
1204 | |||
1205 | // Use newWidth and newHeigh as defined width/height, |
||
1206 | // image should fit the area. |
||
1207 | $this->log("Crop to fit."); |
||
1208 | $ratio = ($ratioWidth < $ratioHeight) ? $ratioWidth : $ratioHeight; |
||
1209 | $this->cropWidth = round($width / $ratio); |
||
1210 | $this->cropHeight = round($height / $ratio); |
||
1211 | $this->log("Crop width, height, ratio: $this->cropWidth x $this->cropHeight ($ratio)."); |
||
1212 | |||
1213 | } elseif ($this->fillToFit) { |
||
1214 | |||
1215 | // Use newWidth and newHeigh as defined width/height, |
||
1216 | // image should fit the area. |
||
1217 | $this->log("Fill to fit."); |
||
1218 | $ratio = ($ratioWidth < $ratioHeight) ? $ratioHeight : $ratioWidth; |
||
1219 | $this->fillWidth = round($width / $ratio); |
||
1220 | $this->fillHeight = round($height / $ratio); |
||
1221 | $this->log("Fill width, height, ratio: $this->fillWidth x $this->fillHeight ($ratio)."); |
||
1222 | } |
||
1223 | } |
||
1224 | } |
||
1225 | |||
1226 | // Crop, ensure to set new width and height |
||
1227 | if ($this->crop) { |
||
1228 | $this->log("Crop."); |
||
1229 | $this->newWidth = round(isset($this->newWidth) ? $this->newWidth : $this->crop['width']); |
||
1230 | $this->newHeight = round(isset($this->newHeight) ? $this->newHeight : $this->crop['height']); |
||
1231 | } |
||
1232 | |||
1233 | // Fill to fit, ensure to set new width and height |
||
1234 | /*if ($this->fillToFit) { |
||
1235 | $this->log("FillToFit."); |
||
1236 | $this->newWidth = round(isset($this->newWidth) ? $this->newWidth : $this->crop['width']); |
||
1237 | $this->newHeight = round(isset($this->newHeight) ? $this->newHeight : $this->crop['height']); |
||
1238 | }*/ |
||
1239 | |||
1240 | // No new height or width is set, use existing measures. |
||
1241 | $this->newWidth = round(isset($this->newWidth) ? $this->newWidth : $this->width); |
||
1242 | $this->newHeight = round(isset($this->newHeight) ? $this->newHeight : $this->height); |
||
1243 | $this->log("Calculated new width x height as {$this->newWidth} x {$this->newHeight}."); |
||
1244 | |||
1245 | return $this; |
||
1246 | } |
||
1247 | |||
1248 | |||
1249 | |||
1250 | /** |
||
1251 | * Re-calculate image dimensions when original image dimension has changed. |
||
1252 | * |
||
1253 | * @return $this |
||
1254 | */ |
||
1255 | public function reCalculateDimensions() |
||
1256 | { |
||
1257 | $this->log("Re-calculate image dimensions, newWidth x newHeigh was: " . $this->newWidth . " x " . $this->newHeight); |
||
1258 | |||
1259 | $this->newWidth = $this->newWidthOrig; |
||
1260 | $this->newHeight = $this->newHeightOrig; |
||
1261 | $this->crop = $this->cropOrig; |
||
1262 | |||
1263 | $this->initDimensions() |
||
1264 | ->calculateNewWidthAndHeight(); |
||
1265 | |||
1266 | return $this; |
||
1267 | } |
||
1268 | |||
1269 | |||
1270 | |||
1271 | /** |
||
1272 | * Set extension for filename to save as. |
||
1273 | * |
||
1274 | * @param string $saveas extension to save image as |
||
0 ignored issues
–
show
There is no parameter named
$saveas . Did you maybe mean $saveAs ?
This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit. Consider the following example. The parameter /**
* @param array $germany
* @param array $ireland
*/
function finale($germany, $island) {
return "2:1";
}
The most likely cause is that the parameter was changed, but the annotation was not. ![]() |
|||
1275 | * |
||
1276 | * @return $this |
||
1277 | */ |
||
1278 | public function setSaveAsExtension($saveAs = null) |
||
1279 | { |
||
1280 | if (isset($saveAs)) { |
||
1281 | $saveAs = strtolower($saveAs); |
||
1282 | $this->checkFileExtension($saveAs); |
||
1283 | $this->saveAs = $saveAs; |
||
1284 | $this->extension = $saveAs; |
||
1285 | } |
||
1286 | |||
1287 | $this->log("Prepare to save image as: " . $this->extension); |
||
1288 | |||
1289 | return $this; |
||
1290 | } |
||
1291 | |||
1292 | |||
1293 | |||
1294 | /** |
||
1295 | * Set JPEG quality to use when saving image |
||
1296 | * |
||
1297 | * @param int $quality as the quality to set. |
||
1298 | * |
||
1299 | * @return $this |
||
1300 | */ |
||
1301 | public function setJpegQuality($quality = null) |
||
1302 | { |
||
1303 | if ($quality) { |
||
0 ignored issues
–
show
The expression
$quality 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
![]() |
|||
1304 | 2 | $this->useQuality = true; |
|
1305 | } |
||
1306 | 2 | ||
1307 | 2 | $this->quality = isset($quality) |
|
1308 | 2 | ? $quality |
|
1309 | 2 | : self::JPEG_QUALITY_DEFAULT; |
|
1310 | 2 | ||
1311 | 2 | (is_numeric($this->quality) and $this->quality > 0 and $this->quality <= 100) |
|
1312 | 2 | or $this->raiseError('Quality not in range.'); |
|
1313 | 2 | ||
1314 | 2 | $this->log("Setting JPEG quality to {$this->quality}."); |
|
1315 | 2 | ||
1316 | 2 | return $this; |
|
1317 | } |
||
1318 | 2 | ||
1319 | 2 | ||
1320 | |||
1321 | 2 | /** |
|
1322 | 2 | * Set PNG compressen algorithm to use when saving image |
|
1323 | * |
||
1324 | * @param int $compress as the algorithm to use. |
||
1325 | * |
||
1326 | 2 | * @return $this |
|
1327 | 2 | */ |
|
1328 | public function setPngCompression($compress = null) |
||
1329 | 2 | { |
|
1330 | 2 | if ($compress) { |
|
0 ignored issues
–
show
The expression
$compress 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
![]() |
|||
1331 | 2 | $this->useCompress = true; |
|
1332 | } |
||
1333 | 2 | ||
1334 | 2 | $this->compress = isset($compress) |
|
1335 | 2 | ? $compress |
|
1336 | : self::PNG_COMPRESSION_DEFAULT; |
||
1337 | 2 | ||
1338 | 2 | (is_numeric($this->compress) and $this->compress >= -1 and $this->compress <= 9) |
|
1339 | or $this->raiseError('Quality not in range.'); |
||
1340 | |||
1341 | $this->log("Setting PNG compression level to {$this->compress}."); |
||
1342 | |||
1343 | return $this; |
||
1344 | } |
||
1345 | |||
1346 | |||
1347 | |||
1348 | /** |
||
1349 | 2 | * Use original image if possible, check options which affects image processing. |
|
1350 | 2 | * |
|
1351 | 2 | * @param boolean $useOrig default is to use original if possible, else set to false. |
|
1352 | 2 | * |
|
1353 | * @return $this |
||
1354 | 2 | */ |
|
1355 | public function useOriginalIfPossible($useOrig = true) |
||
1356 | 2 | { |
|
1357 | 2 | if ($useOrig |
|
1358 | 2 | && ($this->newWidth == $this->width) |
|
1359 | && ($this->newHeight == $this->height) |
||
1360 | 2 | && !$this->area |
|
1361 | 2 | && !$this->crop |
|
0 ignored issues
–
show
The expression
$this->crop 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 ![]() |
|||
1362 | && !$this->cropToFit |
||
1363 | && !$this->fillToFit |
||
1364 | && !$this->filters |
||
1365 | 2 | && !$this->sharpen |
|
1366 | 2 | && !$this->emboss |
|
1367 | && !$this->blur |
||
1368 | && !$this->convolve |
||
1369 | && !$this->palette |
||
1370 | 2 | && !$this->useQuality |
|
1371 | 2 | && !$this->useCompress |
|
1372 | && !$this->saveAs |
||
1373 | && !$this->rotateBefore |
||
1374 | && !$this->rotateAfter |
||
1375 | && !$this->autoRotate |
||
1376 | && !$this->bgColor |
||
1377 | 2 | && ($this->upscale === self::UPSCALE_DEFAULT) |
|
1378 | 2 | && !$this->lossy |
|
1379 | 2 | ) { |
|
1380 | 2 | $this->log("Using original image."); |
|
1381 | 2 | $this->output($this->pathToImage); |
|
1382 | 2 | } |
|
1383 | 2 | ||
1384 | return $this; |
||
1385 | 2 | } |
|
1386 | |||
1387 | |||
1388 | |||
1389 | /** |
||
1390 | * Generate filename to save file in cache. |
||
1391 | * |
||
1392 | * @param string $base as optional basepath for storing file. |
||
1393 | * @param boolean $useSubdir use or skip the subdir part when creating the |
||
1394 | * filename. |
||
1395 | * @param string $prefix to add as part of filename |
||
1396 | * |
||
1397 | * @return $this |
||
1398 | */ |
||
1399 | public function generateFilename($base = null, $useSubdir = true, $prefix = null) |
||
1400 | { |
||
1401 | $filename = basename($this->pathToImage); |
||
1402 | $cropToFit = $this->cropToFit ? '_cf' : null; |
||
1403 | $fillToFit = $this->fillToFit ? '_ff' : null; |
||
1404 | $crop_x = $this->crop_x ? "_x{$this->crop_x}" : null; |
||
1405 | $crop_y = $this->crop_y ? "_y{$this->crop_y}" : null; |
||
1406 | $scale = $this->scale ? "_s{$this->scale}" : null; |
||
1407 | $bgColor = $this->bgColor ? "_bgc{$this->bgColor}" : null; |
||
1408 | $quality = $this->quality ? "_q{$this->quality}" : null; |
||
1409 | $compress = $this->compress ? "_co{$this->compress}" : null; |
||
1410 | $rotateBefore = $this->rotateBefore ? "_rb{$this->rotateBefore}" : null; |
||
1411 | $rotateAfter = $this->rotateAfter ? "_ra{$this->rotateAfter}" : null; |
||
1412 | $lossy = $this->lossy ? "_l" : null; |
||
1413 | $interlace = $this->interlace ? "_i" : null; |
||
1414 | |||
1415 | $saveAs = $this->normalizeFileExtension(); |
||
1416 | $saveAs = $saveAs ? "_$saveAs" : null; |
||
1417 | |||
1418 | $copyStrat = null; |
||
1419 | if ($this->copyStrategy === self::RESIZE) { |
||
1420 | $copyStrat = "_rs"; |
||
1421 | } |
||
1422 | |||
1423 | $width = $this->newWidth ? '_' . $this->newWidth : null; |
||
1424 | $height = $this->newHeight ? '_' . $this->newHeight : null; |
||
1425 | |||
1426 | $offset = isset($this->offset) |
||
1427 | ? '_o' . $this->offset['top'] . '-' . $this->offset['right'] . '-' . $this->offset['bottom'] . '-' . $this->offset['left'] |
||
1428 | : null; |
||
1429 | |||
1430 | $crop = $this->crop |
||
1431 | ? '_c' . $this->crop['width'] . '-' . $this->crop['height'] . '-' . $this->crop['start_x'] . '-' . $this->crop['start_y'] |
||
1432 | : null; |
||
1433 | |||
1434 | $filters = null; |
||
1435 | if (isset($this->filters)) { |
||
1436 | foreach ($this->filters as $filter) { |
||
1437 | if (is_array($filter)) { |
||
1438 | $filters .= "_f{$filter['id']}"; |
||
1439 | for ($i=1; $i<=$filter['argc']; $i++) { |
||
1440 | $filters .= "-".$filter["arg{$i}"]; |
||
1441 | } |
||
1442 | } |
||
1443 | } |
||
1444 | } |
||
1445 | |||
1446 | $sharpen = $this->sharpen ? 's' : null; |
||
1447 | $emboss = $this->emboss ? 'e' : null; |
||
1448 | $blur = $this->blur ? 'b' : null; |
||
1449 | $palette = $this->palette ? 'p' : null; |
||
1450 | |||
1451 | $autoRotate = $this->autoRotate ? 'ar' : null; |
||
1452 | |||
1453 | $optimize = $this->jpegOptimize ? 'o' : null; |
||
1454 | $optimize .= $this->pngFilter ? 'f' : null; |
||
1455 | $optimize .= $this->pngDeflate ? 'd' : null; |
||
1456 | |||
1457 | $convolve = null; |
||
1458 | if ($this->convolve) { |
||
1459 | $convolve = '_conv' . preg_replace('/[^a-zA-Z0-9]/', '', $this->convolve); |
||
1460 | } |
||
1461 | |||
1462 | $upscale = null; |
||
1463 | if ($this->upscale !== self::UPSCALE_DEFAULT) { |
||
1464 | $upscale = '_nu'; |
||
1465 | } |
||
1466 | |||
1467 | $subdir = null; |
||
1468 | if ($useSubdir === true) { |
||
1469 | $subdir = str_replace('/', '-', dirname($this->imageSrc)); |
||
1470 | $subdir = ($subdir == '.') ? '_.' : $subdir; |
||
1471 | $subdir .= '_'; |
||
1472 | } |
||
1473 | |||
1474 | $file = $prefix . $subdir . $filename . $width . $height |
||
1475 | . $offset . $crop . $cropToFit . $fillToFit |
||
1476 | . $crop_x . $crop_y . $upscale |
||
1477 | . $quality . $filters . $sharpen . $emboss . $blur . $palette |
||
1478 | . $optimize . $compress |
||
1479 | . $scale . $rotateBefore . $rotateAfter . $autoRotate . $bgColor |
||
1480 | . $convolve . $copyStrat . $lossy . $interlace . $saveAs; |
||
1481 | |||
1482 | return $this->setTarget($file, $base); |
||
1483 | } |
||
1484 | |||
1485 | |||
1486 | |||
1487 | /** |
||
1488 | * Use cached version of image, if possible. |
||
1489 | * |
||
1490 | * @param boolean $useCache is default true, set to false to avoid using cached object. |
||
1491 | * |
||
1492 | * @return $this |
||
1493 | */ |
||
1494 | public function useCacheIfPossible($useCache = true) |
||
1495 | { |
||
1496 | if ($useCache && is_readable($this->cacheFileName)) { |
||
1497 | $fileTime = filemtime($this->pathToImage); |
||
1498 | $cacheTime = filemtime($this->cacheFileName); |
||
1499 | |||
1500 | if ($fileTime <= $cacheTime) { |
||
1501 | if ($this->useCache) { |
||
1502 | if ($this->verbose) { |
||
1503 | $this->log("Use cached file."); |
||
1504 | $this->log("Cached image filesize: " . filesize($this->cacheFileName) . " bytes."); |
||
1505 | } |
||
1506 | $this->output($this->cacheFileName, $this->outputFormat); |
||
1507 | } else { |
||
1508 | $this->log("Cache is valid but ignoring it by intention."); |
||
1509 | } |
||
1510 | } else { |
||
1511 | $this->log("Original file is modified, ignoring cache."); |
||
1512 | } |
||
1513 | } else { |
||
1514 | $this->log("Cachefile does not exists or ignoring it."); |
||
1515 | } |
||
1516 | |||
1517 | return $this; |
||
1518 | } |
||
1519 | |||
1520 | |||
1521 | |||
1522 | /** |
||
1523 | * Load image from disk. Try to load image without verbose error message, |
||
1524 | * if fail, load again and display error messages. |
||
1525 | * |
||
1526 | * @param string $src of image. |
||
1527 | * @param string $dir as base directory where images are. |
||
1528 | * |
||
1529 | * @return $this |
||
1530 | * |
||
1531 | */ |
||
1532 | public function load($src = null, $dir = null) |
||
1533 | { |
||
1534 | if (isset($src)) { |
||
1535 | $this->setSource($src, $dir); |
||
1536 | } |
||
1537 | |||
1538 | $this->loadImageDetails(); |
||
1539 | |||
1540 | if ($this->fileType === IMG_WEBP) { |
||
1541 | $this->image = imagecreatefromwebp($this->pathToImage); |
||
1542 | } else { |
||
1543 | $imageAsString = file_get_contents($this->pathToImage); |
||
1544 | $this->image = imagecreatefromstring($imageAsString); |
||
1545 | } |
||
1546 | if ($this->image === false) { |
||
1547 | throw new Exception("Could not load image."); |
||
1548 | } |
||
1549 | |||
1550 | /* Removed v0.7.7 |
||
1551 | if (image_type_to_mime_type($this->fileType) == 'image/png') { |
||
1552 | $type = $this->getPngType(); |
||
1553 | $hasFewColors = imagecolorstotal($this->image); |
||
1554 | |||
1555 | if ($type == self::PNG_RGB_PALETTE || ($hasFewColors > 0 && $hasFewColors <= 256)) { |
||
1556 | if ($this->verbose) { |
||
1557 | $this->log("Handle this image as a palette image."); |
||
1558 | } |
||
1559 | $this->palette = true; |
||
1560 | } |
||
1561 | } |
||
1562 | */ |
||
1563 | |||
1564 | if ($this->verbose) { |
||
1565 | $this->log("### Image successfully loaded from file."); |
||
1566 | $this->log(" imageistruecolor() : " . (imageistruecolor($this->image) ? 'true' : 'false')); |
||
1567 | $this->log(" imagecolorstotal() : " . imagecolorstotal($this->image)); |
||
1568 | $this->log(" Number of colors in image = " . $this->colorsTotal($this->image)); |
||
1569 | $index = imagecolortransparent($this->image); |
||
1570 | $this->log(" Detected transparent color = " . ($index >= 0 ? implode(", ", imagecolorsforindex($this->image, $index)) : "NONE") . " at index = $index"); |
||
1571 | } |
||
1572 | |||
1573 | return $this; |
||
1574 | } |
||
1575 | |||
1576 | |||
1577 | |||
1578 | /** |
||
1579 | * Get the type of PNG image. |
||
1580 | * |
||
1581 | * @param string $filename to use instead of default. |
||
1582 | * |
||
1583 | * @return int as the type of the png-image |
||
1584 | * |
||
1585 | */ |
||
1586 | public function getPngType($filename = null) |
||
1587 | { |
||
1588 | $filename = $filename ? $filename : $this->pathToImage; |
||
1589 | |||
1590 | $pngType = ord(file_get_contents($filename, false, null, 25, 1)); |
||
1591 | |||
1592 | if ($this->verbose) { |
||
1593 | $this->log("Checking png type of: " . $filename); |
||
1594 | $this->log($this->getPngTypeAsString($pngType)); |
||
1595 | } |
||
1596 | |||
1597 | return $pngType; |
||
1598 | } |
||
1599 | |||
1600 | |||
1601 | |||
1602 | /** |
||
1603 | * Get the type of PNG image as a verbose string. |
||
1604 | * |
||
1605 | * @param integer $type to use, default is to check the type. |
||
0 ignored issues
–
show
There is no parameter named
$type . Was it maybe removed?
This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. Consider the following example. The parameter /**
* @param array $germany
* @param array $island
* @param array $italy
*/
function finale($germany, $island) {
return "2:1";
}
The most likely cause is that the parameter was removed, but the annotation was not. ![]() |
|||
1606 | * @param string $filename to use instead of default. |
||
1607 | * |
||
1608 | * @return int as the type of the png-image |
||
1609 | * |
||
1610 | */ |
||
1611 | private function getPngTypeAsString($pngType = null, $filename = null) |
||
1612 | { |
||
1613 | if ($filename || !$pngType) { |
||
0 ignored issues
–
show
The expression
$filename of type string|null is loosely compared to true ; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.
In PHP, under loose comparison (like For '' == false // true
'' == null // true
'ab' == false // false
'ab' == null // false
// It is often better to use strict comparison
'' === false // false
'' === null // false
![]() |
|||
1614 | $pngType = $this->getPngType($filename); |
||
1615 | } |
||
1616 | |||
1617 | $index = imagecolortransparent($this->image); |
||
1618 | $transparent = null; |
||
1619 | if ($index != -1) { |
||
1620 | $transparent = " (transparent)"; |
||
1621 | } |
||
1622 | |||
1623 | switch ($pngType) { |
||
1624 | |||
1625 | case self::PNG_GREYSCALE: |
||
1626 | $text = "PNG is type 0, Greyscale$transparent"; |
||
1627 | break; |
||
1628 | |||
1629 | case self::PNG_RGB: |
||
1630 | $text = "PNG is type 2, RGB$transparent"; |
||
1631 | break; |
||
1632 | |||
1633 | case self::PNG_RGB_PALETTE: |
||
1634 | $text = "PNG is type 3, RGB with palette$transparent"; |
||
1635 | break; |
||
1636 | |||
1637 | case self::PNG_GREYSCALE_ALPHA: |
||
1638 | $text = "PNG is type 4, Greyscale with alpha channel"; |
||
1639 | break; |
||
1640 | |||
1641 | case self::PNG_RGB_ALPHA: |
||
1642 | $text = "PNG is type 6, RGB with alpha channel (PNG 32-bit)"; |
||
1643 | break; |
||
1644 | |||
1645 | default: |
||
1646 | $text = "PNG is UNKNOWN type, is it really a PNG image?"; |
||
1647 | } |
||
1648 | |||
1649 | return $text; |
||
1650 | } |
||
1651 | |||
1652 | |||
1653 | |||
1654 | |||
1655 | /** |
||
1656 | * Calculate number of colors in an image. |
||
1657 | * |
||
1658 | * @param resource $im the image. |
||
1659 | * |
||
1660 | * @return int |
||
1661 | */ |
||
1662 | private function colorsTotal($im) |
||
1663 | { |
||
1664 | if (imageistruecolor($im)) { |
||
1665 | $this->log("Colors as true color."); |
||
1666 | $h = imagesy($im); |
||
1667 | $w = imagesx($im); |
||
1668 | $c = array(); |
||
1669 | for ($x=0; $x < $w; $x++) { |
||
1670 | for ($y=0; $y < $h; $y++) { |
||
1671 | @$c['c'.imagecolorat($im, $x, $y)]++; |
||
0 ignored issues
–
show
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.');
}
![]() |
|||
1672 | } |
||
1673 | } |
||
1674 | return count($c); |
||
1675 | } else { |
||
1676 | $this->log("Colors as palette."); |
||
1677 | return imagecolorstotal($im); |
||
1678 | } |
||
1679 | } |
||
1680 | |||
1681 | |||
1682 | |||
1683 | /** |
||
1684 | * Preprocess image before rezising it. |
||
1685 | * |
||
1686 | * @return $this |
||
1687 | */ |
||
1688 | public function preResize() |
||
1689 | { |
||
1690 | $this->log("### Pre-process before resizing"); |
||
1691 | |||
1692 | // Rotate image |
||
1693 | if ($this->rotateBefore) { |
||
1694 | $this->log("Rotating image."); |
||
1695 | $this->rotate($this->rotateBefore, $this->bgColor) |
||
1696 | ->reCalculateDimensions(); |
||
1697 | } |
||
1698 | |||
1699 | // Auto-rotate image |
||
1700 | if ($this->autoRotate) { |
||
1701 | $this->log("Auto rotating image."); |
||
1702 | $this->rotateExif() |
||
1703 | ->reCalculateDimensions(); |
||
1704 | } |
||
1705 | |||
1706 | // Scale the original image before starting |
||
1707 | if (isset($this->scale)) { |
||
1708 | $this->log("Scale by {$this->scale}%"); |
||
1709 | $newWidth = $this->width * $this->scale / 100; |
||
1710 | $newHeight = $this->height * $this->scale / 100; |
||
1711 | $img = $this->CreateImageKeepTransparency($newWidth, $newHeight); |
||
1712 | imagecopyresampled($img, $this->image, 0, 0, 0, 0, $newWidth, $newHeight, $this->width, $this->height); |
||
1713 | $this->image = $img; |
||
1714 | $this->width = $newWidth; |
||
1715 | $this->height = $newHeight; |
||
1716 | } |
||
1717 | |||
1718 | return $this; |
||
1719 | } |
||
1720 | |||
1721 | |||
1722 | |||
1723 | /** |
||
1724 | * Resize or resample the image while resizing. |
||
1725 | * |
||
1726 | * @param int $strategy as CImage::RESIZE or CImage::RESAMPLE |
||
1727 | * |
||
1728 | * @return $this |
||
1729 | */ |
||
1730 | public function setCopyResizeStrategy($strategy) |
||
1731 | { |
||
1732 | $this->copyStrategy = $strategy; |
||
1733 | return $this; |
||
1734 | } |
||
1735 | |||
1736 | |||
1737 | |||
1738 | /** |
||
1739 | * Resize and or crop the image. |
||
1740 | * |
||
1741 | * @return void |
||
1742 | */ |
||
1743 | public function imageCopyResampled($dst_image, $src_image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h) |
||
1744 | { |
||
1745 | if($this->copyStrategy == self::RESIZE) { |
||
1746 | $this->log("Copy by resize"); |
||
1747 | imagecopyresized($dst_image, $src_image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h); |
||
1748 | } else { |
||
1749 | $this->log("Copy by resample"); |
||
1750 | imagecopyresampled($dst_image, $src_image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h); |
||
1751 | } |
||
1752 | } |
||
1753 | |||
1754 | |||
1755 | |||
1756 | /** |
||
1757 | * Resize and or crop the image. |
||
1758 | * |
||
1759 | * @return $this |
||
1760 | */ |
||
1761 | public function resize() |
||
1762 | { |
||
1763 | |||
1764 | $this->log("### Starting to Resize()"); |
||
1765 | $this->log("Upscale = '$this->upscale'"); |
||
1766 | |||
1767 | // Only use a specified area of the image, $this->offset is defining the area to use |
||
1768 | if (isset($this->offset)) { |
||
1769 | |||
1770 | $this->log("Offset for area to use, cropping it width={$this->offset['width']}, height={$this->offset['height']}, start_x={$this->offset['left']}, start_y={$this->offset['top']}"); |
||
1771 | $img = $this->CreateImageKeepTransparency($this->offset['width'], $this->offset['height']); |
||
1772 | imagecopy($img, $this->image, 0, 0, $this->offset['left'], $this->offset['top'], $this->offset['width'], $this->offset['height']); |
||
1773 | $this->image = $img; |
||
1774 | $this->width = $this->offset['width']; |
||
1775 | $this->height = $this->offset['height']; |
||
1776 | } |
||
1777 | |||
1778 | if ($this->crop) { |
||
0 ignored issues
–
show
The expression
$this->crop 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 ![]() |
|||
1779 | |||
1780 | // Do as crop, take only part of image |
||
1781 | $this->log("Cropping area width={$this->crop['width']}, height={$this->crop['height']}, start_x={$this->crop['start_x']}, start_y={$this->crop['start_y']}"); |
||
1782 | $img = $this->CreateImageKeepTransparency($this->crop['width'], $this->crop['height']); |
||
1783 | imagecopy($img, $this->image, 0, 0, $this->crop['start_x'], $this->crop['start_y'], $this->crop['width'], $this->crop['height']); |
||
1784 | $this->image = $img; |
||
1785 | $this->width = $this->crop['width']; |
||
1786 | $this->height = $this->crop['height']; |
||
1787 | } |
||
1788 | |||
1789 | if (!$this->upscale) { |
||
1790 | // Consider rewriting the no-upscale code to fit within this if-statement, |
||
1791 | // likely to be more readable code. |
||
1792 | // The code is more or leass equal in below crop-to-fit, fill-to-fit and stretch |
||
1793 | } |
||
1794 | |||
1795 | if ($this->cropToFit) { |
||
1796 | |||
1797 | // Resize by crop to fit |
||
1798 | $this->log("Resizing using strategy - Crop to fit"); |
||
1799 | |||
1800 | if (!$this->upscale |
||
1801 | && ($this->width < $this->newWidth || $this->height < $this->newHeight)) { |
||
1802 | $this->log("Resizing - smaller image, do not upscale."); |
||
1803 | |||
1804 | $posX = 0; |
||
1805 | $posY = 0; |
||
1806 | $cropX = 0; |
||
1807 | $cropY = 0; |
||
1808 | |||
1809 | if ($this->newWidth > $this->width) { |
||
1810 | $posX = round(($this->newWidth - $this->width) / 2); |
||
1811 | } |
||
1812 | if ($this->newWidth < $this->width) { |
||
1813 | $cropX = round(($this->width/2) - ($this->newWidth/2)); |
||
1814 | } |
||
1815 | |||
1816 | if ($this->newHeight > $this->height) { |
||
1817 | $posY = round(($this->newHeight - $this->height) / 2); |
||
1818 | } |
||
1819 | if ($this->newHeight < $this->height) { |
||
1820 | $cropY = round(($this->height/2) - ($this->newHeight/2)); |
||
1821 | } |
||
1822 | $this->log(" cwidth: $this->cropWidth"); |
||
1823 | $this->log(" cheight: $this->cropHeight"); |
||
1824 | $this->log(" nwidth: $this->newWidth"); |
||
1825 | $this->log(" nheight: $this->newHeight"); |
||
1826 | $this->log(" width: $this->width"); |
||
1827 | $this->log(" height: $this->height"); |
||
1828 | $this->log(" posX: $posX"); |
||
1829 | $this->log(" posY: $posY"); |
||
1830 | $this->log(" cropX: $cropX"); |
||
1831 | $this->log(" cropY: $cropY"); |
||
1832 | |||
1833 | $imageResized = $this->CreateImageKeepTransparency($this->newWidth, $this->newHeight); |
||
1834 | imagecopy($imageResized, $this->image, $posX, $posY, $cropX, $cropY, $this->width, $this->height); |
||
1835 | } else { |
||
1836 | $cropX = round(($this->cropWidth/2) - ($this->newWidth/2)); |
||
1837 | $cropY = round(($this->cropHeight/2) - ($this->newHeight/2)); |
||
1838 | $imgPreCrop = $this->CreateImageKeepTransparency($this->cropWidth, $this->cropHeight); |
||
1839 | $imageResized = $this->CreateImageKeepTransparency($this->newWidth, $this->newHeight); |
||
1840 | $this->imageCopyResampled($imgPreCrop, $this->image, 0, 0, 0, 0, $this->cropWidth, $this->cropHeight, $this->width, $this->height); |
||
1841 | imagecopy($imageResized, $imgPreCrop, 0, 0, $cropX, $cropY, $this->newWidth, $this->newHeight); |
||
1842 | } |
||
1843 | |||
1844 | $this->image = $imageResized; |
||
1845 | $this->width = $this->newWidth; |
||
1846 | $this->height = $this->newHeight; |
||
1847 | |||
1848 | } elseif ($this->fillToFit) { |
||
1849 | |||
1850 | // Resize by fill to fit |
||
1851 | $this->log("Resizing using strategy - Fill to fit"); |
||
1852 | |||
1853 | $posX = 0; |
||
1854 | $posY = 0; |
||
1855 | |||
1856 | $ratioOrig = $this->width / $this->height; |
||
1857 | $ratioNew = $this->newWidth / $this->newHeight; |
||
1858 | |||
1859 | // Check ratio for landscape or portrait |
||
1860 | if ($ratioOrig < $ratioNew) { |
||
1861 | $posX = round(($this->newWidth - $this->fillWidth) / 2); |
||
1862 | } else { |
||
1863 | $posY = round(($this->newHeight - $this->fillHeight) / 2); |
||
1864 | } |
||
1865 | |||
1866 | if (!$this->upscale |
||
1867 | && ($this->width < $this->newWidth && $this->height < $this->newHeight) |
||
1868 | ) { |
||
1869 | |||
1870 | $this->log("Resizing - smaller image, do not upscale."); |
||
1871 | $posX = round(($this->newWidth - $this->width) / 2); |
||
1872 | $posY = round(($this->newHeight - $this->height) / 2); |
||
1873 | $imageResized = $this->CreateImageKeepTransparency($this->newWidth, $this->newHeight); |
||
1874 | imagecopy($imageResized, $this->image, $posX, $posY, 0, 0, $this->width, $this->height); |
||
1875 | |||
1876 | } else { |
||
1877 | $imgPreFill = $this->CreateImageKeepTransparency($this->fillWidth, $this->fillHeight); |
||
1878 | $imageResized = $this->CreateImageKeepTransparency($this->newWidth, $this->newHeight); |
||
1879 | $this->imageCopyResampled($imgPreFill, $this->image, 0, 0, 0, 0, $this->fillWidth, $this->fillHeight, $this->width, $this->height); |
||
1880 | imagecopy($imageResized, $imgPreFill, $posX, $posY, 0, 0, $this->fillWidth, $this->fillHeight); |
||
1881 | } |
||
1882 | |||
1883 | $this->image = $imageResized; |
||
1884 | $this->width = $this->newWidth; |
||
1885 | $this->height = $this->newHeight; |
||
1886 | |||
1887 | } elseif (!($this->newWidth == $this->width && $this->newHeight == $this->height)) { |
||
1888 | |||
1889 | // Resize it |
||
1890 | $this->log("Resizing, new height and/or width"); |
||
1891 | |||
1892 | if (!$this->upscale |
||
1893 | && ($this->width < $this->newWidth || $this->height < $this->newHeight) |
||
1894 | ) { |
||
1895 | $this->log("Resizing - smaller image, do not upscale."); |
||
1896 | |||
1897 | if (!$this->keepRatio) { |
||
1898 | $this->log("Resizing - stretch to fit selected."); |
||
1899 | |||
1900 | $posX = 0; |
||
1901 | $posY = 0; |
||
1902 | $cropX = 0; |
||
1903 | $cropY = 0; |
||
1904 | |||
1905 | if ($this->newWidth > $this->width && $this->newHeight > $this->height) { |
||
1906 | $posX = round(($this->newWidth - $this->width) / 2); |
||
1907 | $posY = round(($this->newHeight - $this->height) / 2); |
||
1908 | } elseif ($this->newWidth > $this->width) { |
||
1909 | $posX = round(($this->newWidth - $this->width) / 2); |
||
1910 | $cropY = round(($this->height - $this->newHeight) / 2); |
||
1911 | } elseif ($this->newHeight > $this->height) { |
||
1912 | $posY = round(($this->newHeight - $this->height) / 2); |
||
1913 | $cropX = round(($this->width - $this->newWidth) / 2); |
||
1914 | } |
||
1915 | |||
1916 | $imageResized = $this->CreateImageKeepTransparency($this->newWidth, $this->newHeight); |
||
1917 | imagecopy($imageResized, $this->image, $posX, $posY, $cropX, $cropY, $this->width, $this->height); |
||
1918 | $this->image = $imageResized; |
||
1919 | $this->width = $this->newWidth; |
||
1920 | $this->height = $this->newHeight; |
||
1921 | } |
||
1922 | } else { |
||
1923 | $imageResized = $this->CreateImageKeepTransparency($this->newWidth, $this->newHeight); |
||
1924 | $this->imageCopyResampled($imageResized, $this->image, 0, 0, 0, 0, $this->newWidth, $this->newHeight, $this->width, $this->height); |
||
1925 | $this->image = $imageResized; |
||
1926 | $this->width = $this->newWidth; |
||
1927 | $this->height = $this->newHeight; |
||
1928 | } |
||
1929 | } |
||
1930 | |||
1931 | return $this; |
||
1932 | } |
||
1933 | |||
1934 | |||
1935 | |||
1936 | /** |
||
1937 | * Postprocess image after rezising image. |
||
1938 | * |
||
1939 | * @return $this |
||
1940 | */ |
||
1941 | public function postResize() |
||
1942 | { |
||
1943 | $this->log("### Post-process after resizing"); |
||
1944 | |||
1945 | // Rotate image |
||
1946 | if ($this->rotateAfter) { |
||
1947 | $this->log("Rotating image."); |
||
1948 | $this->rotate($this->rotateAfter, $this->bgColor); |
||
1949 | } |
||
1950 | |||
1951 | // Apply filters |
||
1952 | if (isset($this->filters) && is_array($this->filters)) { |
||
1953 | |||
1954 | foreach ($this->filters as $filter) { |
||
1955 | $this->log("Applying filter {$filter['type']}."); |
||
1956 | |||
1957 | switch ($filter['argc']) { |
||
1958 | |||
1959 | case 0: |
||
1960 | imagefilter($this->image, $filter['type']); |
||
1961 | break; |
||
1962 | |||
1963 | case 1: |
||
1964 | imagefilter($this->image, $filter['type'], $filter['arg1']); |
||
1965 | break; |
||
1966 | |||
1967 | case 2: |
||
1968 | imagefilter($this->image, $filter['type'], $filter['arg1'], $filter['arg2']); |
||
1969 | break; |
||
1970 | |||
1971 | case 3: |
||
1972 | imagefilter($this->image, $filter['type'], $filter['arg1'], $filter['arg2'], $filter['arg3']); |
||
1973 | break; |
||
1974 | |||
1975 | case 4: |
||
1976 | imagefilter($this->image, $filter['type'], $filter['arg1'], $filter['arg2'], $filter['arg3'], $filter['arg4']); |
||
1977 | break; |
||
1978 | } |
||
1979 | } |
||
1980 | } |
||
1981 | |||
1982 | // Convert to palette image |
||
1983 | if ($this->palette) { |
||
1984 | $this->log("Converting to palette image."); |
||
1985 | $this->trueColorToPalette(); |
||
1986 | } |
||
1987 | |||
1988 | // Blur the image |
||
1989 | if ($this->blur) { |
||
1990 | $this->log("Blur."); |
||
1991 | $this->blurImage(); |
||
1992 | } |
||
1993 | |||
1994 | // Emboss the image |
||
1995 | if ($this->emboss) { |
||
1996 | $this->log("Emboss."); |
||
1997 | $this->embossImage(); |
||
1998 | } |
||
1999 | |||
2000 | // Sharpen the image |
||
2001 | if ($this->sharpen) { |
||
2002 | $this->log("Sharpen."); |
||
2003 | $this->sharpenImage(); |
||
2004 | } |
||
2005 | |||
2006 | // Custom convolution |
||
2007 | if ($this->convolve) { |
||
2008 | //$this->log("Convolve: " . $this->convolve); |
||
2009 | $this->imageConvolution(); |
||
2010 | } |
||
2011 | |||
2012 | return $this; |
||
2013 | } |
||
2014 | |||
2015 | |||
2016 | |||
2017 | /** |
||
2018 | * Rotate image using angle. |
||
2019 | * |
||
2020 | * @param float $angle to rotate image. |
||
2021 | * @param int $anglebgColor to fill image with if needed. |
||
0 ignored issues
–
show
There is no parameter named
$anglebgColor . Did you maybe mean $bgColor ?
This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit. Consider the following example. The parameter /**
* @param array $germany
* @param array $ireland
*/
function finale($germany, $island) {
return "2:1";
}
The most likely cause is that the parameter was changed, but the annotation was not. ![]() |
|||
2022 | * |
||
2023 | * @return $this |
||
2024 | */ |
||
2025 | public function rotate($angle, $bgColor) |
||
0 ignored issues
–
show
|
|||
2026 | { |
||
2027 | $this->log("Rotate image " . $angle . " degrees with filler color."); |
||
2028 | |||
2029 | $color = $this->getBackgroundColor(); |
||
2030 | $this->image = imagerotate($this->image, $angle, $color); |
||
2031 | |||
2032 | $this->width = imagesx($this->image); |
||
2033 | $this->height = imagesy($this->image); |
||
2034 | |||
2035 | $this->log("New image dimension width x height: " . $this->width . " x " . $this->height); |
||
2036 | |||
2037 | return $this; |
||
2038 | } |
||
2039 | |||
2040 | |||
2041 | |||
2042 | /** |
||
2043 | * Rotate image using information in EXIF. |
||
2044 | * |
||
2045 | * @return $this |
||
2046 | */ |
||
2047 | public function rotateExif() |
||
2048 | { |
||
2049 | if (!in_array($this->fileType, array(IMAGETYPE_JPEG, IMAGETYPE_TIFF_II, IMAGETYPE_TIFF_MM))) { |
||
2050 | $this->log("Autorotate ignored, EXIF not supported by this filetype."); |
||
2051 | return $this; |
||
2052 | } |
||
2053 | |||
2054 | $exif = exif_read_data($this->pathToImage); |
||
2055 | |||
2056 | if (!empty($exif['Orientation'])) { |
||
2057 | switch ($exif['Orientation']) { |
||
2058 | case 3: |
||
2059 | $this->log("Autorotate 180."); |
||
2060 | $this->rotate(180, $this->bgColor); |
||
2061 | break; |
||
2062 | |||
2063 | case 6: |
||
2064 | $this->log("Autorotate -90."); |
||
2065 | $this->rotate(-90, $this->bgColor); |
||
2066 | break; |
||
2067 | |||
2068 | case 8: |
||
2069 | $this->log("Autorotate 90."); |
||
2070 | $this->rotate(90, $this->bgColor); |
||
2071 | break; |
||
2072 | |||
2073 | default: |
||
2074 | $this->log("Autorotate ignored, unknown value as orientation."); |
||
2075 | } |
||
2076 | } else { |
||
2077 | $this->log("Autorotate ignored, no orientation in EXIF."); |
||
2078 | } |
||
2079 | |||
2080 | return $this; |
||
2081 | } |
||
2082 | |||
2083 | |||
2084 | |||
2085 | /** |
||
2086 | * Convert true color image to palette image, keeping alpha. |
||
2087 | * http://stackoverflow.com/questions/5752514/how-to-convert-png-to-8-bit-png-using-php-gd-library |
||
2088 | * |
||
2089 | * @return void |
||
2090 | */ |
||
2091 | public function trueColorToPalette() |
||
2092 | { |
||
2093 | $img = imagecreatetruecolor($this->width, $this->height); |
||
2094 | $bga = imagecolorallocatealpha($img, 0, 0, 0, 127); |
||
2095 | imagecolortransparent($img, $bga); |
||
2096 | imagefill($img, 0, 0, $bga); |
||
2097 | imagecopy($img, $this->image, 0, 0, 0, 0, $this->width, $this->height); |
||
2098 | imagetruecolortopalette($img, false, 255); |
||
2099 | imagesavealpha($img, true); |
||
2100 | |||
2101 | if (imageistruecolor($this->image)) { |
||
2102 | $this->log("Matching colors with true color image."); |
||
2103 | imagecolormatch($this->image, $img); |
||
2104 | } |
||
2105 | |||
2106 | $this->image = $img; |
||
2107 | } |
||
2108 | |||
2109 | |||
2110 | |||
2111 | /** |
||
2112 | * Sharpen image using image convolution. |
||
2113 | * |
||
2114 | * @return $this |
||
2115 | */ |
||
2116 | public function sharpenImage() |
||
2117 | { |
||
2118 | $this->imageConvolution('sharpen'); |
||
2119 | return $this; |
||
2120 | } |
||
2121 | |||
2122 | |||
2123 | |||
2124 | /** |
||
2125 | * Emboss image using image convolution. |
||
2126 | * |
||
2127 | * @return $this |
||
2128 | */ |
||
2129 | public function embossImage() |
||
2130 | { |
||
2131 | $this->imageConvolution('emboss'); |
||
2132 | return $this; |
||
2133 | } |
||
2134 | |||
2135 | |||
2136 | |||
2137 | /** |
||
2138 | * Blur image using image convolution. |
||
2139 | * |
||
2140 | * @return $this |
||
2141 | */ |
||
2142 | public function blurImage() |
||
2143 | { |
||
2144 | $this->imageConvolution('blur'); |
||
2145 | return $this; |
||
2146 | } |
||
2147 | |||
2148 | |||
2149 | |||
2150 | /** |
||
2151 | * Create convolve expression and return arguments for image convolution. |
||
2152 | * |
||
2153 | * @param string $expression constant string which evaluates to a list of |
||
2154 | * 11 numbers separated by komma or such a list. |
||
2155 | * |
||
2156 | * @return array as $matrix (3x3), $divisor and $offset |
||
2157 | */ |
||
2158 | public function createConvolveArguments($expression) |
||
2159 | { |
||
2160 | // Check of matching constant |
||
2161 | if (isset($this->convolves[$expression])) { |
||
2162 | $expression = $this->convolves[$expression]; |
||
2163 | } |
||
2164 | |||
2165 | $part = explode(',', $expression); |
||
2166 | $this->log("Creating convolution expressen: $expression"); |
||
2167 | |||
2168 | // Expect list of 11 numbers, split by , and build up arguments |
||
2169 | if (count($part) != 11) { |
||
2170 | throw new Exception( |
||
2171 | "Missmatch in argument convolve. Expected comma-separated string with |
||
2172 | 11 float values. Got $expression." |
||
2173 | ); |
||
2174 | } |
||
2175 | |||
2176 | array_walk($part, function ($item, $key) { |
||
0 ignored issues
–
show
|
|||
2177 | if (!is_numeric($item)) { |
||
2178 | throw new Exception("Argument to convolve expression should be float but is not."); |
||
2179 | } |
||
2180 | }); |
||
2181 | 2 | ||
2182 | return array( |
||
2183 | 2 | array( |
|
2184 | array($part[0], $part[1], $part[2]), |
||
2185 | 2 | array($part[3], $part[4], $part[5]), |
|
2186 | array($part[6], $part[7], $part[8]), |
||
2187 | 2 | ), |
|
2188 | 2 | $part[9], |
|
2189 | 2 | $part[10], |
|
2190 | 2 | ); |
|
2191 | } |
||
2192 | 2 | ||
2193 | |||
2194 | |||
2195 | 2 | /** |
|
2196 | * Add custom expressions (or overwrite existing) for image convolution. |
||
2197 | * |
||
2198 | 2 | * @param array $options Key value array with strings to be converted |
|
2199 | * to convolution expressions. |
||
2200 | * |
||
2201 | * @return $this |
||
2202 | */ |
||
2203 | public function addConvolveExpressions($options) |
||
2204 | { |
||
2205 | $this->convolves = array_merge($this->convolves, $options); |
||
2206 | return $this; |
||
2207 | } |
||
2208 | |||
2209 | |||
2210 | |||
2211 | /** |
||
2212 | * Image convolution. |
||
2213 | * |
||
2214 | * @param string $options A string with 11 float separated by comma. |
||
2215 | 2 | * |
|
2216 | * @return $this |
||
2217 | 2 | */ |
|
2218 | 2 | public function imageConvolution($options = null) |
|
2219 | 2 | { |
|
2220 | 2 | // Use incoming options or use $this. |
|
2221 | $options = $options ? $options : $this->convolve; |
||
2222 | 2 | ||
2223 | 2 | // Treat incoming as string, split by + |
|
2224 | 2 | $this->log("Convolution with '$options'"); |
|
2225 | $options = explode(":", $options); |
||
2226 | 2 | ||
2227 | // Check each option if it matches constant value |
||
2228 | foreach ($options as $option) { |
||
2229 | list($matrix, $divisor, $offset) = $this->createConvolveArguments($option); |
||
2230 | imageconvolution($this->image, $matrix, $divisor, $offset); |
||
2231 | } |
||
2232 | |||
2233 | return $this; |
||
2234 | } |
||
2235 | 2 | ||
2236 | |||
2237 | 2 | ||
2238 | 2 | /** |
|
2239 | 2 | * Set default background color between 000000-FFFFFF or if using |
|
2240 | 2 | * alpha 00000000-FFFFFF7F. |
|
2241 | * |
||
2242 | 2 | * @param string $color as hex value. |
|
2243 | * |
||
2244 | * @return $this |
||
2245 | */ |
||
2246 | public function setDefaultBackgroundColor($color) |
||
2247 | { |
||
2248 | $this->log("Setting default background color to '$color'."); |
||
2249 | |||
2250 | if (!(strlen($color) == 6 || strlen($color) == 8)) { |
||
2251 | throw new Exception( |
||
2252 | "Background color needs a hex value of 6 or 8 |
||
2253 | digits. 000000-FFFFFF or 00000000-FFFFFF7F. |
||
2254 | Current value was: '$color'." |
||
2255 | ); |
||
2256 | } |
||
2257 | |||
2258 | $red = hexdec(substr($color, 0, 2)); |
||
2259 | $green = hexdec(substr($color, 2, 2)); |
||
2260 | $blue = hexdec(substr($color, 4, 2)); |
||
2261 | |||
2262 | $alpha = (strlen($color) == 8) |
||
2263 | ? hexdec(substr($color, 6, 2)) |
||
2264 | : null; |
||
2265 | |||
2266 | if (($red < 0 || $red > 255) |
||
2267 | || ($green < 0 || $green > 255) |
||
2268 | || ($blue < 0 || $blue > 255) |
||
2269 | || ($alpha < 0 || $alpha > 127) |
||
2270 | ) { |
||
2271 | throw new Exception( |
||
2272 | "Background color out of range. Red, green blue |
||
2273 | should be 00-FF and alpha should be 00-7F. |
||
2274 | Current value was: '$color'." |
||
2275 | ); |
||
2276 | } |
||
2277 | |||
2278 | $this->bgColor = strtolower($color); |
||
2279 | $this->bgColorDefault = array( |
||
2280 | 'red' => $red, |
||
2281 | 'green' => $green, |
||
2282 | 'blue' => $blue, |
||
2283 | 'alpha' => $alpha |
||
2284 | 2 | ); |
|
2285 | |||
2286 | return $this; |
||
2287 | 2 | } |
|
2288 | |||
2289 | |||
2290 | 2 | ||
2291 | /** |
||
2292 | * Get the background color. |
||
2293 | * |
||
2294 | * @param resource $img the image to work with or null if using $this->image. |
||
2295 | * |
||
2296 | * @return color value or null if no background color is set. |
||
2297 | */ |
||
2298 | private function getBackgroundColor($img = null) |
||
2299 | { |
||
2300 | $img = isset($img) ? $img : $this->image; |
||
2301 | |||
2302 | if ($this->bgColorDefault) { |
||
0 ignored issues
–
show
The expression
$this->bgColorDefault 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 ![]() |
|||
2303 | |||
2304 | $red = $this->bgColorDefault['red']; |
||
2305 | 2 | $green = $this->bgColorDefault['green']; |
|
2306 | $blue = $this->bgColorDefault['blue']; |
||
2307 | 2 | $alpha = $this->bgColorDefault['alpha']; |
|
2308 | |||
2309 | if ($alpha) { |
||
2310 | $color = imagecolorallocatealpha($img, $red, $green, $blue, $alpha); |
||
2311 | 2 | } else { |
|
2312 | $color = imagecolorallocate($img, $red, $green, $blue); |
||
2313 | } |
||
2314 | |||
2315 | return $color; |
||
2316 | 2 | ||
2317 | } else { |
||
2318 | return 0; |
||
2319 | 2 | } |
|
2320 | 2 | } |
|
2321 | |||
2322 | |||
2323 | 2 | ||
2324 | 2 | /** |
|
2325 | * Create a image and keep transparency for png and gifs. |
||
2326 | * |
||
2327 | * @param int $width of the new image. |
||
2328 | * @param int $height of the new image. |
||
2329 | * |
||
2330 | * @return image resource. |
||
2331 | */ |
||
2332 | private function createImageKeepTransparency($width, $height) |
||
2333 | { |
||
2334 | $this->log("Creating a new working image width={$width}px, height={$height}px."); |
||
2335 | $img = imagecreatetruecolor($width, $height); |
||
2336 | imagealphablending($img, false); |
||
2337 | imagesavealpha($img, true); |
||
2338 | |||
2339 | $index = $this->image |
||
2340 | ? imagecolortransparent($this->image) |
||
2341 | : -1; |
||
2342 | 2 | ||
2343 | if ($index != -1) { |
||
2344 | |||
2345 | imagealphablending($img, true); |
||
2346 | $transparent = imagecolorsforindex($this->image, $index); |
||
2347 | 2 | $color = imagecolorallocatealpha($img, $transparent['red'], $transparent['green'], $transparent['blue'], $transparent['alpha']); |
|
2348 | 2 | imagefill($img, 0, 0, $color); |
|
2349 | 2 | $index = imagecolortransparent($img, $color); |
|
2350 | $this->Log("Detected transparent color = " . implode(", ", $transparent) . " at index = $index"); |
||
2351 | |||
2352 | 2 | } elseif ($this->bgColorDefault) { |
|
0 ignored issues
–
show
The expression
$this->bgColorDefault 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 ![]() |
|||
2353 | 2 | ||
2354 | 2 | $color = $this->getBackgroundColor($img); |
|
2355 | imagefill($img, 0, 0, $color); |
||
2356 | $this->Log("Filling image with background color."); |
||
2357 | 2 | } |
|
2358 | |||
2359 | return $img; |
||
2360 | } |
||
2361 | |||
2362 | |||
2363 | |||
2364 | /** |
||
2365 | * Set optimizing and post-processing options. |
||
2366 | * |
||
2367 | * @param array $options with config for postprocessing with external tools. |
||
2368 | * |
||
2369 | * @return $this |
||
2370 | 2 | */ |
|
2371 | public function setPostProcessingOptions($options) |
||
2372 | { |
||
2373 | if (isset($options['jpeg_optimize']) && $options['jpeg_optimize']) { |
||
2374 | $this->jpegOptimizeCmd = $options['jpeg_optimize_cmd']; |
||
2375 | } else { |
||
2376 | $this->jpegOptimizeCmd = null; |
||
2377 | } |
||
2378 | |||
2379 | if (array_key_exists("png_lossy", $options) |
||
2380 | && $options['png_lossy'] !== false) { |
||
2381 | 2 | $this->pngLossy = $options['png_lossy']; |
|
2382 | 2 | $this->pngLossyCmd = $options['png_lossy_cmd']; |
|
2383 | } else { |
||
2384 | 2 | $this->pngLossyCmd = null; |
|
2385 | } |
||
2386 | |||
2387 | if (isset($options['png_filter']) && $options['png_filter']) { |
||
2388 | $this->pngFilterCmd = $options['png_filter_cmd']; |
||
2389 | } else { |
||
2390 | $this->pngFilterCmd = null; |
||
2391 | } |
||
2392 | |||
2393 | if (isset($options['png_deflate']) && $options['png_deflate']) { |
||
2394 | $this->pngDeflateCmd = $options['png_deflate_cmd']; |
||
2395 | 2 | } else { |
|
2396 | $this->pngDeflateCmd = null; |
||
2397 | } |
||
2398 | |||
2399 | return $this; |
||
2400 | } |
||
2401 | |||
2402 | |||
2403 | |||
2404 | /** |
||
2405 | * Find out the type (file extension) for the image to be saved. |
||
2406 | * |
||
2407 | * @return string as image extension. |
||
2408 | */ |
||
2409 | protected function getTargetImageExtension() |
||
2410 | { |
||
2411 | // switch on mimetype |
||
2412 | if (isset($this->extension)) { |
||
2413 | return strtolower($this->extension); |
||
2414 | } elseif ($this->fileType === IMG_WEBP) { |
||
2415 | return "webp"; |
||
2416 | } |
||
2417 | |||
2418 | return substr(image_type_to_extension($this->fileType), 1); |
||
2419 | } |
||
2420 | |||
2421 | |||
2422 | |||
2423 | /** |
||
2424 | * Save image. |
||
2425 | * |
||
2426 | * @param string $src as target filename. |
||
2427 | * @param string $base as base directory where to store images. |
||
2428 | * @param boolean $overwrite or not, default to always overwrite file. |
||
2429 | * |
||
2430 | * @return $this or false if no folder is set. |
||
2431 | */ |
||
2432 | public function save($src = null, $base = null, $overwrite = true) |
||
2433 | { |
||
2434 | if (isset($src)) { |
||
2435 | $this->setTarget($src, $base); |
||
2436 | } |
||
2437 | |||
2438 | if ($overwrite === false && is_file($this->cacheFileName)) { |
||
2439 | $this->Log("Not overwriting file since its already exists and \$overwrite if false."); |
||
2440 | return; |
||
2441 | } |
||
2442 | |||
2443 | is_writable($this->saveFolder) |
||
2444 | or $this->raiseError('Target directory is not writable.'); |
||
2445 | |||
2446 | $type = $this->getTargetImageExtension(); |
||
2447 | $this->Log("Saving image as " . $type); |
||
2448 | switch($type) { |
||
2449 | |||
2450 | case 'jpeg': |
||
2451 | case 'jpg': |
||
2452 | // Set as interlaced progressive JPEG |
||
2453 | if ($this->interlace) { |
||
2454 | $this->Log("Set JPEG image to be interlaced."); |
||
2455 | $res = imageinterlace($this->image, true); |
||
0 ignored issues
–
show
$res is not used, you could remove the assignment.
This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently. $myVar = 'Value';
$higher = false;
if (rand(1, 6) > 3) {
$higher = true;
} else {
$higher = false;
}
Both the ![]() |
|||
2456 | } |
||
2457 | |||
2458 | $this->Log("Saving image as JPEG to cache using quality = {$this->quality}."); |
||
2459 | imagejpeg($this->image, $this->cacheFileName, $this->quality); |
||
2460 | |||
2461 | // Use JPEG optimize if defined |
||
2462 | if ($this->jpegOptimizeCmd) { |
||
2463 | if ($this->verbose) { |
||
2464 | clearstatcache(); |
||
2465 | $this->log("Filesize before optimize: " . filesize($this->cacheFileName) . " bytes."); |
||
2466 | } |
||
2467 | $res = array(); |
||
2468 | $cmd = $this->jpegOptimizeCmd . " -outfile $this->cacheFileName $this->cacheFileName"; |
||
2469 | exec($cmd, $res); |
||
2470 | $this->log($cmd); |
||
2471 | $this->log($res); |
||
0 ignored issues
–
show
$res is of type null|array , but the function expects a string .
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);
![]() |
|||
2472 | } |
||
2473 | break; |
||
2474 | |||
2475 | case 'gif': |
||
2476 | $this->Log("Saving image as GIF to cache."); |
||
2477 | imagegif($this->image, $this->cacheFileName); |
||
2478 | break; |
||
2479 | |||
2480 | case 'webp': |
||
2481 | $this->Log("Saving image as WEBP to cache using quality = {$this->quality}."); |
||
2482 | imagewebp($this->image, $this->cacheFileName, $this->quality); |
||
2483 | break; |
||
2484 | |||
2485 | case 'png': |
||
2486 | default: |
||
2487 | $this->Log("Saving image as PNG to cache using compression = {$this->compress}."); |
||
2488 | |||
2489 | // Turn off alpha blending and set alpha flag |
||
2490 | imagealphablending($this->image, false); |
||
2491 | imagesavealpha($this->image, true); |
||
2492 | imagepng($this->image, $this->cacheFileName, $this->compress); |
||
2493 | |||
2494 | // Use external program to process lossy PNG, if defined |
||
2495 | $lossyEnabled = $this->pngLossy === true; |
||
2496 | $lossySoftEnabled = $this->pngLossy === null; |
||
2497 | $lossyActiveEnabled = $this->lossy === true; |
||
2498 | if ($lossyEnabled || ($lossySoftEnabled && $lossyActiveEnabled)) { |
||
2499 | if ($this->verbose) { |
||
2500 | clearstatcache(); |
||
2501 | $this->log("Lossy enabled: $lossyEnabled"); |
||
2502 | $this->log("Lossy soft enabled: $lossySoftEnabled"); |
||
2503 | $this->Log("Filesize before lossy optimize: " . filesize($this->cacheFileName) . " bytes."); |
||
2504 | } |
||
2505 | $res = array(); |
||
2506 | $cmd = $this->pngLossyCmd . " $this->cacheFileName $this->cacheFileName"; |
||
2507 | exec($cmd, $res); |
||
2508 | $this->Log($cmd); |
||
2509 | $this->Log($res); |
||
0 ignored issues
–
show
$res is of type null|array , but the function expects a string .
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);
![]() |
|||
2510 | } |
||
2511 | |||
2512 | // Use external program to filter PNG, if defined |
||
2513 | if ($this->pngFilterCmd) { |
||
2514 | if ($this->verbose) { |
||
2515 | clearstatcache(); |
||
2516 | $this->Log("Filesize before filter optimize: " . filesize($this->cacheFileName) . " bytes."); |
||
2517 | } |
||
2518 | $res = array(); |
||
2519 | $cmd = $this->pngFilterCmd . " $this->cacheFileName"; |
||
2520 | exec($cmd, $res); |
||
2521 | $this->Log($cmd); |
||
2522 | $this->Log($res); |
||
0 ignored issues
–
show
$res is of type null|array , but the function expects a string .
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);
![]() |
|||
2523 | } |
||
2524 | |||
2525 | // Use external program to deflate PNG, if defined |
||
2526 | if ($this->pngDeflateCmd) { |
||
2527 | if ($this->verbose) { |
||
2528 | clearstatcache(); |
||
2529 | $this->Log("Filesize before deflate optimize: " . filesize($this->cacheFileName) . " bytes."); |
||
2530 | } |
||
2531 | $res = array(); |
||
2532 | $cmd = $this->pngDeflateCmd . " $this->cacheFileName"; |
||
2533 | exec($cmd, $res); |
||
2534 | $this->Log($cmd); |
||
2535 | $this->Log($res); |
||
0 ignored issues
–
show
$res is of type null|array , but the function expects a string .
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);
![]() |
|||
2536 | } |
||
2537 | break; |
||
2538 | } |
||
2539 | |||
2540 | if ($this->verbose) { |
||
2541 | clearstatcache(); |
||
2542 | $this->log("Saved image to cache."); |
||
2543 | $this->log(" Cached image filesize: " . filesize($this->cacheFileName) . " bytes."); |
||
2544 | $this->log(" imageistruecolor() : " . (imageistruecolor($this->image) ? 'true' : 'false')); |
||
2545 | $this->log(" imagecolorstotal() : " . imagecolorstotal($this->image)); |
||
2546 | $this->log(" Number of colors in image = " . $this->ColorsTotal($this->image)); |
||
2547 | $index = imagecolortransparent($this->image); |
||
2548 | $this->log(" Detected transparent color = " . ($index > 0 ? implode(", ", imagecolorsforindex($this->image, $index)) : "NONE") . " at index = $index"); |
||
2549 | } |
||
2550 | |||
2551 | return $this; |
||
2552 | } |
||
2553 | |||
2554 | |||
2555 | |||
2556 | /** |
||
2557 | * Convert image from one colorpsace/color profile to sRGB without |
||
2558 | * color profile. |
||
2559 | * |
||
2560 | * @param string $src of image. |
||
2561 | * @param string $dir as base directory where images are. |
||
2562 | * @param string $cache as base directory where to store images. |
||
2563 | * @param string $iccFile filename of colorprofile. |
||
2564 | * @param boolean $useCache or not, default to always use cache. |
||
2565 | * |
||
2566 | * @return string | boolean false if no conversion else the converted |
||
2567 | * filename. |
||
2568 | */ |
||
2569 | public function convert2sRGBColorSpace($src, $dir, $cache, $iccFile, $useCache = true) |
||
2570 | { |
||
2571 | if ($this->verbose) { |
||
2572 | $this->log("# Converting image to sRGB colorspace."); |
||
2573 | } |
||
2574 | |||
2575 | if (!class_exists("Imagick")) { |
||
2576 | $this->log(" Ignoring since Imagemagick is not installed."); |
||
2577 | return false; |
||
2578 | } |
||
2579 | |||
2580 | // Prepare |
||
2581 | $this->setSaveFolder($cache) |
||
2582 | ->setSource($src, $dir) |
||
2583 | ->generateFilename(null, false, 'srgb_'); |
||
2584 | |||
2585 | // Check if the cached version is accurate. |
||
2586 | if ($useCache && is_readable($this->cacheFileName)) { |
||
2587 | $fileTime = filemtime($this->pathToImage); |
||
2588 | $cacheTime = filemtime($this->cacheFileName); |
||
2589 | |||
2590 | if ($fileTime <= $cacheTime) { |
||
2591 | $this->log(" Using cached version: " . $this->cacheFileName); |
||
2592 | return $this->cacheFileName; |
||
2593 | } |
||
2594 | } |
||
2595 | |||
2596 | // Only covert if cachedir is writable |
||
2597 | if (is_writable($this->saveFolder)) { |
||
2598 | // Load file and check if conversion is needed |
||
2599 | $image = new Imagick($this->pathToImage); |
||
2600 | $colorspace = $image->getImageColorspace(); |
||
2601 | $this->log(" Current colorspace: " . $colorspace); |
||
2602 | |||
2603 | $profiles = $image->getImageProfiles('*', false); |
||
2604 | $hasICCProfile = (array_search('icc', $profiles) !== false); |
||
2605 | $this->log(" Has ICC color profile: " . ($hasICCProfile ? "YES" : "NO")); |
||
2606 | |||
2607 | if ($colorspace != Imagick::COLORSPACE_SRGB || $hasICCProfile) { |
||
2608 | $this->log(" Converting to sRGB."); |
||
2609 | |||
2610 | $sRGBicc = file_get_contents($iccFile); |
||
2611 | $image->profileImage('icc', $sRGBicc); |
||
2612 | |||
2613 | $image->transformImageColorspace(Imagick::COLORSPACE_SRGB); |
||
2614 | $image->writeImage($this->cacheFileName); |
||
2615 | return $this->cacheFileName; |
||
2616 | } |
||
2617 | } |
||
2618 | |||
2619 | return false; |
||
2620 | } |
||
2621 | |||
2622 | |||
2623 | |||
2624 | /** |
||
2625 | * Create a hard link, as an alias, to the cached file. |
||
2626 | * |
||
2627 | 7 | * @param string $alias where to store the link, |
|
2628 | * filename without extension. |
||
2629 | 7 | * |
|
2630 | * @return $this |
||
2631 | */ |
||
2632 | public function linkToCacheFile($alias) |
||
2633 | 7 | { |
|
2634 | if ($alias === null) { |
||
2635 | $this->log("Ignore creating alias."); |
||
2636 | return $this; |
||
2637 | } |
||
2638 | |||
2639 | if (is_readable($alias)) { |
||
2640 | unlink($alias); |
||
0 ignored issues
–
show
$alias can contain request data and is used in file manipulation context(s) leading to a potential security vulnerability.
General Strategies to prevent injectionIn general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:
if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
throw new \InvalidArgumentException('This input is not allowed.');
}
For numeric data, we recommend to explicitly cast the data: $sanitized = (integer) $tainted;
![]() |
|||
2641 | } |
||
2642 | |||
2643 | $res = link($this->cacheFileName, $alias); |
||
2644 | |||
2645 | if ($res) { |
||
2646 | $this->log("Created an alias as: $alias"); |
||
2647 | } else { |
||
2648 | $this->log("Failed to create the alias: $alias"); |
||
2649 | } |
||
2650 | |||
2651 | return $this; |
||
2652 | } |
||
2653 | |||
2654 | |||
2655 | |||
2656 | /** |
||
2657 | * Add HTTP header for output together with image. |
||
2658 | * |
||
2659 | * @param string $type the header type such as "Cache-Control" |
||
2660 | * @param string $value the value to use |
||
2661 | * |
||
2662 | * @return void |
||
2663 | */ |
||
2664 | public function addHTTPHeader($type, $value) |
||
2665 | { |
||
2666 | $this->HTTPHeader[$type] = $value; |
||
2667 | } |
||
2668 | |||
2669 | |||
2670 | |||
2671 | /** |
||
2672 | * Output image to browser using caching. |
||
2673 | * |
||
2674 | * @param string $file to read and output, default is to |
||
2675 | * use $this->cacheFileName |
||
2676 | * @param string $format set to json to output file as json |
||
2677 | * object with details |
||
2678 | * |
||
2679 | * @return void |
||
2680 | */ |
||
2681 | public function output($file = null, $format = null) |
||
2682 | { |
||
2683 | if (is_null($file)) { |
||
2684 | $file = $this->cacheFileName; |
||
2685 | } |
||
2686 | |||
2687 | if (is_null($format)) { |
||
2688 | $format = $this->outputFormat; |
||
2689 | } |
||
2690 | |||
2691 | $this->log("### Output"); |
||
2692 | $this->log("Output format is: $format"); |
||
2693 | |||
2694 | if (!$this->verbose && $format == 'json') { |
||
2695 | header('Content-type: application/json'); |
||
2696 | echo $this->json($file); |
||
2697 | exit; |
||
2698 | } elseif ($format == 'ascii') { |
||
2699 | header('Content-type: text/plain'); |
||
2700 | echo $this->ascii($file); |
||
0 ignored issues
–
show
Security
Cross-Site Scripting
introduced
by
$this->ascii($file) can contain request data and is used in output context(s) leading to a potential security vulnerability.
2 paths for user data to reach this point
Preventing Cross-Site-Scripting AttacksCross-Site-Scripting allows an attacker to inject malicious code into your website - in particular Javascript code, and have that code executed with the privileges of a visiting user. This can be used to obtain data, or perform actions on behalf of that visiting user. In order to prevent this, make sure to escape all user-provided data:
// for HTML
$sanitized = htmlentities($tainted, ENT_QUOTES);
// for URLs
$sanitized = urlencode($tainted);
General Strategies to prevent injectionIn general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:
if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
throw new \InvalidArgumentException('This input is not allowed.');
}
For numeric data, we recommend to explicitly cast the data: $sanitized = (integer) $tainted;
![]() |
|||
2701 | exit; |
||
2702 | } |
||
2703 | |||
2704 | $this->log("Outputting image: $file"); |
||
2705 | |||
2706 | // Get image modification time |
||
2707 | clearstatcache(); |
||
2708 | $lastModified = filemtime($file); |
||
2709 | $lastModifiedFormat = "D, d M Y H:i:s"; |
||
2710 | $gmdate = gmdate($lastModifiedFormat, $lastModified); |
||
2711 | |||
2712 | if (!$this->verbose) { |
||
2713 | $header = "Last-Modified: $gmdate GMT"; |
||
2714 | header($header); |
||
2715 | $this->fastTrackCache->addHeader($header); |
||
2716 | $this->fastTrackCache->setLastModified($lastModified); |
||
2717 | } |
||
2718 | |||
2719 | foreach ($this->HTTPHeader as $key => $val) { |
||
2720 | $header = "$key: $val"; |
||
2721 | header($header); |
||
2722 | $this->fastTrackCache->addHeader($header); |
||
2723 | } |
||
2724 | |||
2725 | if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) |
||
2726 | && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) == $lastModified) { |
||
2727 | |||
2728 | if ($this->verbose) { |
||
2729 | $this->log("304 not modified"); |
||
2730 | $this->verboseOutput(); |
||
2731 | exit; |
||
2732 | } |
||
2733 | |||
2734 | header("HTTP/1.0 304 Not Modified"); |
||
2735 | if (CIMAGE_DEBUG) { |
||
2736 | trace(__CLASS__ . " 304"); |
||
2737 | } |
||
2738 | |||
2739 | } else { |
||
2740 | |||
2741 | $this->loadImageDetails($file); |
||
2742 | $mime = $this->getMimeType(); |
||
2743 | $size = filesize($file); |
||
2744 | |||
2745 | if ($this->verbose) { |
||
2746 | $this->log("Last-Modified: " . $gmdate . " GMT"); |
||
2747 | $this->log("Content-type: " . $mime); |
||
2748 | $this->log("Content-length: " . $size); |
||
2749 | $this->verboseOutput(); |
||
2750 | |||
2751 | if (is_null($this->verboseFileName)) { |
||
2752 | exit; |
||
2753 | } |
||
2754 | } |
||
2755 | |||
2756 | $header = "Content-type: $mime"; |
||
2757 | header($header); |
||
2758 | $this->fastTrackCache->addHeaderOnOutput($header); |
||
2759 | |||
2760 | $header = "Content-length: $size"; |
||
2761 | header($header); |
||
2762 | $this->fastTrackCache->addHeaderOnOutput($header); |
||
2763 | |||
2764 | $this->fastTrackCache->setSource($file); |
||
2765 | $this->fastTrackCache->writeToCache(); |
||
2766 | if (CIMAGE_DEBUG) { |
||
2767 | trace(__CLASS__ . " 200"); |
||
2768 | } |
||
2769 | readfile($file); |
||
2770 | } |
||
2771 | |||
2772 | exit; |
||
2773 | } |
||
2774 | |||
2775 | |||
2776 | |||
2777 | /** |
||
2778 | * Create a JSON object from the image details. |
||
2779 | * |
||
2780 | * @param string $file the file to output. |
||
2781 | * |
||
2782 | * @return string json-encoded representation of the image. |
||
2783 | */ |
||
2784 | public function json($file = null) |
||
2785 | { |
||
2786 | $file = $file ? $file : $this->cacheFileName; |
||
2787 | |||
2788 | $details = array(); |
||
2789 | |||
2790 | clearstatcache(); |
||
2791 | |||
2792 | $details['src'] = $this->imageSrc; |
||
2793 | $lastModified = filemtime($this->pathToImage); |
||
2794 | $details['srcGmdate'] = gmdate("D, d M Y H:i:s", $lastModified); |
||
2795 | |||
2796 | $details['cache'] = basename($this->cacheFileName); |
||
2797 | $lastModified = filemtime($this->cacheFileName); |
||
2798 | $details['cacheGmdate'] = gmdate("D, d M Y H:i:s", $lastModified); |
||
2799 | |||
2800 | $this->load($file); |
||
2801 | |||
2802 | $details['filename'] = basename($file); |
||
2803 | $details['mimeType'] = $this->getMimeType($this->fileType); |
||
0 ignored issues
–
show
The call to
CImage::getMimeType() has too many arguments starting with $this->fileType .
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. In this case you can add the ![]() |
|||
2804 | $details['width'] = $this->width; |
||
2805 | $details['height'] = $this->height; |
||
2806 | $details['aspectRatio'] = round($this->width / $this->height, 3); |
||
2807 | $details['size'] = filesize($file); |
||
2808 | $details['colors'] = $this->colorsTotal($this->image); |
||
2809 | $details['includedFiles'] = count(get_included_files()); |
||
2810 | $details['memoryPeek'] = round(memory_get_peak_usage()/1024/1024, 3) . " MB" ; |
||
2811 | $details['memoryCurrent'] = round(memory_get_usage()/1024/1024, 3) . " MB"; |
||
2812 | $details['memoryLimit'] = ini_get('memory_limit'); |
||
2813 | |||
2814 | if (isset($_SERVER['REQUEST_TIME_FLOAT'])) { |
||
2815 | $details['loadTime'] = (string) round((microtime(true) - $_SERVER['REQUEST_TIME_FLOAT']), 3) . "s"; |
||
2816 | } |
||
2817 | |||
2818 | if ($details['mimeType'] == 'image/png') { |
||
2819 | $details['pngType'] = $this->getPngTypeAsString(null, $file); |
||
2820 | } |
||
2821 | |||
2822 | $options = null; |
||
2823 | if (defined("JSON_PRETTY_PRINT") && defined("JSON_UNESCAPED_SLASHES")) { |
||
2824 | $options = JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES; |
||
2825 | } |
||
2826 | |||
2827 | return json_encode($details, $options); |
||
2828 | } |
||
2829 | |||
2830 | |||
2831 | |||
2832 | /** |
||
2833 | * Set options for creating ascii version of image. |
||
2834 | * |
||
2835 | * @param array $options empty to use default or set options to change. |
||
2836 | * |
||
2837 | * @return void. |
||
0 ignored issues
–
show
The doc-type
void. could not be parsed: Unknown type name "void." at position 0. (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. ![]() |
|||
2838 | */ |
||
2839 | public function setAsciiOptions($options = array()) |
||
2840 | { |
||
2841 | $this->asciiOptions = $options; |
||
2842 | } |
||
2843 | |||
2844 | |||
2845 | |||
2846 | /** |
||
2847 | * Create an ASCII version from the image details. |
||
2848 | * |
||
2849 | * @param string $file the file to output. |
||
2850 | * |
||
2851 | * @return string ASCII representation of the image. |
||
2852 | */ |
||
2853 | public function ascii($file = null) |
||
2854 | { |
||
2855 | $file = $file ? $file : $this->cacheFileName; |
||
2856 | |||
2857 | $asciiArt = new CAsciiArt(); |
||
2858 | $asciiArt->setOptions($this->asciiOptions); |
||
2859 | return $asciiArt->createFromFile($file); |
||
2860 | } |
||
2861 | |||
2862 | |||
2863 | |||
2864 | /** |
||
2865 | * Log an event if verbose mode. |
||
2866 | * |
||
2867 | * @param string $message to log. |
||
2868 | * |
||
2869 | * @return this |
||
2870 | */ |
||
2871 | public function log($message) |
||
2872 | { |
||
2873 | if ($this->verbose) { |
||
2874 | $this->log[] = $message; |
||
2875 | } |
||
2876 | |||
2877 | return $this; |
||
2878 | } |
||
2879 | |||
2880 | |||
2881 | |||
2882 | /** |
||
2883 | * Do verbose output to a file. |
||
2884 | * |
||
2885 | * @param string $fileName where to write the verbose output. |
||
2886 | * |
||
2887 | * @return void |
||
2888 | */ |
||
2889 | public function setVerboseToFile($fileName) |
||
2890 | { |
||
2891 | $this->log("Setting verbose output to file."); |
||
2892 | $this->verboseFileName = $fileName; |
||
2893 | } |
||
2894 | |||
2895 | |||
2896 | |||
2897 | /** |
||
2898 | * Do verbose output and print out the log and the actual images. |
||
2899 | * |
||
2900 | * @return void |
||
2901 | */ |
||
2902 | private function verboseOutput() |
||
2903 | { |
||
2904 | $log = null; |
||
2905 | $this->log("### Summary of verbose log"); |
||
2906 | $this->log("As JSON: \n" . $this->json()); |
||
2907 | $this->log("Memory peak: " . round(memory_get_peak_usage() /1024/1024) . "M"); |
||
2908 | $this->log("Memory limit: " . ini_get('memory_limit')); |
||
2909 | |||
2910 | $included = get_included_files(); |
||
2911 | $this->log("Included files: " . count($included)); |
||
2912 | |||
2913 | foreach ($this->log as $val) { |
||
2914 | if (is_array($val)) { |
||
2915 | foreach ($val as $val1) { |
||
2916 | $log .= htmlentities($val1) . '<br/>'; |
||
2917 | } |
||
2918 | } else { |
||
2919 | $log .= htmlentities($val) . '<br/>'; |
||
2920 | } |
||
2921 | } |
||
2922 | |||
2923 | if (!is_null($this->verboseFileName)) { |
||
2924 | file_put_contents( |
||
2925 | $this->verboseFileName, |
||
2926 | str_replace("<br/>", "\n", $log) |
||
0 ignored issues
–
show
str_replace('<br/>', ' ', $log) can contain request data and is used in file manipulation context(s) leading to a potential security vulnerability.
2 paths for user data to reach this point
General Strategies to prevent injectionIn general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:
if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
throw new \InvalidArgumentException('This input is not allowed.');
}
For numeric data, we recommend to explicitly cast the data: $sanitized = (integer) $tainted;
![]() |
|||
2927 | ); |
||
2928 | } else { |
||
2929 | echo <<<EOD |
||
2930 | <h1>CImage Verbose Output</h1> |
||
2931 | <pre>{$log}</pre> |
||
2932 | EOD; |
||
2933 | } |
||
2934 | } |
||
2935 | |||
2936 | |||
2937 | |||
2938 | /** |
||
2939 | * Raise error, enables to implement a selection of error methods. |
||
2940 | * |
||
2941 | * @param string $message the error message to display. |
||
2942 | * |
||
2943 | * @return void |
||
2944 | * @throws Exception |
||
2945 | */ |
||
2946 | private function raiseError($message) |
||
2947 | { |
||
2948 | throw new Exception($message); |
||
2949 | } |
||
2950 | } |
||
2951 |
This check looks for type mismatches where the missing type is
false
. This is usually indicative of an error condtion.Consider the follow example
This function either returns a new
DateTime
object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returnedfalse
before passing on the value to another function or method that may not be able to handle afalse
.