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 | */ |
||
10 | |||
11 | /** |
||
12 | * Custom exception handler. |
||
13 | */ |
||
14 | set_exception_handler(function ($exception) { |
||
15 | errorPage( |
||
16 | "<p><b>img.php: Uncaught exception:</b> <p>" |
||
17 | . $exception->getMessage() |
||
18 | . "</p><pre>" |
||
19 | . $exception->getTraceAsString() |
||
20 | . "</pre>", |
||
21 | 500 |
||
22 | ); |
||
23 | }); |
||
24 | |||
25 | |||
26 | |||
27 | /** |
||
28 | * Get configuration options from file, if the file exists, else use $config |
||
29 | * if its defined or create an empty $config. |
||
30 | */ |
||
31 | $configFile = __DIR__.'/'.basename(__FILE__, '.php').'_config.php'; |
||
32 | |||
33 | if (is_file($configFile)) { |
||
34 | $config = require $configFile; |
||
35 | } elseif (!isset($config)) { |
||
36 | $config = array(); |
||
37 | } |
||
38 | |||
39 | // Make CIMAGE_DEBUG false by default, if not already defined |
||
40 | if (!defined("CIMAGE_DEBUG")) { |
||
41 | define("CIMAGE_DEBUG", false); |
||
42 | } |
||
43 | |||
44 | |||
45 | |||
46 | /** |
||
47 | * Setup the autoloader, but not when using a bundle. |
||
48 | */ |
||
49 | if (!defined("CIMAGE_BUNDLE")) { |
||
50 | if (!isset($config["autoloader"])) { |
||
51 | die("CImage: Missing autoloader."); |
||
52 | } |
||
53 | |||
54 | require $config["autoloader"]; |
||
55 | } |
||
56 | |||
57 | |||
58 | |||
59 | /** |
||
60 | * verbose, v - do a verbose dump of what happens |
||
61 | * vf - do verbose dump to file |
||
62 | */ |
||
63 | $verbose = getDefined(array('verbose', 'v'), true, false); |
||
64 | $verboseFile = getDefined('vf', true, false); |
||
65 | verbose("img.php version = " . CIMAGE_VERSION); |
||
66 | |||
67 | |||
68 | |||
69 | /** |
||
70 | * status - do a verbose dump of the configuration |
||
71 | */ |
||
72 | $status = getDefined('status', true, false); |
||
73 | |||
74 | |||
75 | |||
76 | /** |
||
77 | * Set mode as strict, production or development. |
||
78 | * Default is production environment. |
||
79 | */ |
||
80 | $mode = getConfig('mode', 'production'); |
||
81 | |||
82 | // Settings for any mode |
||
83 | set_time_limit(20); |
||
84 | ini_set('gd.jpeg_ignore_warning', 1); |
||
85 | |||
86 | if (!extension_loaded('gd')) { |
||
87 | errorPage("Extension gd is not loaded.", 500); |
||
88 | } |
||
89 | |||
90 | // Specific settings for each mode |
||
91 | if ($mode == 'strict') { |
||
92 | |||
93 | error_reporting(0); |
||
94 | ini_set('display_errors', 0); |
||
95 | ini_set('log_errors', 1); |
||
96 | $verbose = false; |
||
97 | $status = false; |
||
98 | $verboseFile = false; |
||
99 | |||
100 | } elseif ($mode == 'production') { |
||
101 | |||
102 | error_reporting(-1); |
||
103 | ini_set('display_errors', 0); |
||
104 | ini_set('log_errors', 1); |
||
105 | $verbose = false; |
||
106 | $status = false; |
||
107 | $verboseFile = false; |
||
108 | |||
109 | } elseif ($mode == 'development') { |
||
110 | |||
111 | error_reporting(-1); |
||
112 | ini_set('display_errors', 1); |
||
113 | ini_set('log_errors', 0); |
||
114 | $verboseFile = false; |
||
115 | |||
116 | } elseif ($mode == 'test') { |
||
117 | |||
118 | error_reporting(-1); |
||
119 | ini_set('display_errors', 1); |
||
120 | ini_set('log_errors', 0); |
||
121 | |||
122 | } else { |
||
123 | errorPage("Unknown mode: $mode", 500); |
||
124 | } |
||
125 | |||
126 | verbose("mode = $mode"); |
||
127 | verbose("error log = " . ini_get('error_log')); |
||
128 | |||
129 | |||
130 | |||
131 | /** |
||
132 | * Set default timezone if not set or if its set in the config-file. |
||
133 | */ |
||
134 | $defaultTimezone = getConfig('default_timezone', null); |
||
135 | |||
136 | if ($defaultTimezone) { |
||
137 | date_default_timezone_set($defaultTimezone); |
||
138 | } elseif (!ini_get('default_timezone')) { |
||
139 | date_default_timezone_set('UTC'); |
||
140 | } |
||
141 | |||
142 | |||
143 | |||
144 | /** |
||
145 | * Check if passwords are configured, used and match. |
||
146 | * Options decide themself if they require passwords to be used. |
||
147 | */ |
||
148 | $pwdConfig = getConfig('password', false); |
||
149 | $pwdAlways = getConfig('password_always', false); |
||
150 | $pwdType = getConfig('password_type', 'text'); |
||
151 | $pwd = get(array('password', 'pwd'), null); |
||
152 | |||
153 | // Check if passwords match, if configured to use passwords |
||
154 | $passwordMatch = null; |
||
155 | if ($pwd) { |
||
156 | switch ($pwdType) { |
||
157 | case 'md5': |
||
158 | $passwordMatch = ($pwdConfig === md5($pwd)); |
||
159 | break; |
||
160 | case 'hash': |
||
161 | $passwordMatch = password_verify($pwd, $pwdConfig); |
||
162 | break; |
||
163 | case 'text': |
||
164 | $passwordMatch = ($pwdConfig === $pwd); |
||
165 | break; |
||
166 | default: |
||
167 | $passwordMatch = false; |
||
168 | } |
||
169 | } |
||
170 | |||
171 | if ($pwdAlways && $passwordMatch !== true) { |
||
172 | errorPage("Password required and does not match or exists.", 403); |
||
173 | } |
||
174 | |||
175 | verbose("password match = $passwordMatch"); |
||
176 | |||
177 | |||
178 | |||
179 | /** |
||
180 | * Prevent hotlinking, leeching, of images by controlling who access them |
||
181 | * from where. |
||
182 | * |
||
183 | */ |
||
184 | $allowHotlinking = getConfig('allow_hotlinking', true); |
||
185 | $hotlinkingWhitelist = getConfig('hotlinking_whitelist', array()); |
||
186 | |||
187 | $serverName = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : null; |
||
188 | $referer = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : null; |
||
189 | $refererHost = parse_url($referer, PHP_URL_HOST); |
||
190 | |||
191 | if (!$allowHotlinking) { |
||
192 | if ($passwordMatch) { |
||
193 | ; // Always allow when password match |
||
194 | verbose("Hotlinking since passwordmatch"); |
||
195 | } elseif ($passwordMatch === false) { |
||
196 | errorPage("Hotlinking/leeching not allowed when password missmatch.", 403); |
||
197 | } elseif (!$referer) { |
||
198 | errorPage("Hotlinking/leeching not allowed and referer is missing.", 403); |
||
199 | } elseif (strcmp($serverName, $refererHost) == 0) { |
||
200 | ; // Allow when serverName matches refererHost |
||
201 | verbose("Hotlinking disallowed but serverName matches refererHost."); |
||
202 | } elseif (!empty($hotlinkingWhitelist)) { |
||
203 | $whitelist = new CWhitelist(); |
||
204 | $allowedByWhitelist = $whitelist->check($refererHost, $hotlinkingWhitelist); |
||
0 ignored issues
–
show
|
|||
205 | |||
206 | if ($allowedByWhitelist) { |
||
207 | verbose("Hotlinking/leeching allowed by whitelist."); |
||
208 | } else { |
||
209 | errorPage("Hotlinking/leeching not allowed by whitelist. Referer: $referer.", 403); |
||
210 | } |
||
211 | |||
212 | } else { |
||
213 | errorPage("Hotlinking/leeching not allowed.", 403); |
||
214 | } |
||
215 | } |
||
216 | |||
217 | verbose("allow_hotlinking = $allowHotlinking"); |
||
218 | verbose("referer = $referer"); |
||
219 | verbose("referer host = $refererHost"); |
||
220 | |||
221 | |||
222 | |||
223 | /** |
||
224 | * Create the class for the image. |
||
225 | */ |
||
226 | $CImage = getConfig('CImage', 'CImage'); |
||
227 | $img = new $CImage(); |
||
228 | $img->setVerbose($verbose || $verboseFile); |
||
229 | |||
230 | |||
231 | |||
232 | /** |
||
233 | * Get the cachepath from config. |
||
234 | */ |
||
235 | $CCache = getConfig('CCache', 'CCache'); |
||
236 | $cachePath = getConfig('cache_path', __DIR__ . '/../cache/'); |
||
237 | $cache = new $CCache(); |
||
238 | $cache->setDir($cachePath); |
||
239 | |||
240 | |||
241 | |||
242 | /** |
||
243 | * no-cache, nc - skip the cached version and process and create a new version in cache. |
||
244 | */ |
||
245 | $useCache = getDefined(array('no-cache', 'nc'), false, true); |
||
246 | |||
247 | verbose("use cache = $useCache"); |
||
248 | |||
249 | |||
250 | |||
251 | /** |
||
252 | * Prepare fast track cache for swriting cache items. |
||
253 | */ |
||
254 | $fastTrackCache = "fasttrack"; |
||
255 | $allowFastTrackCache = getConfig('fast_track_allow', false); |
||
256 | |||
257 | $CFastTrackCache = getConfig('CFastTrackCache', 'CFastTrackCache'); |
||
258 | $ftc = new $CFastTrackCache(); |
||
259 | $ftc->setCacheDir($cache->getPathToSubdir($fastTrackCache)) |
||
260 | ->enable($allowFastTrackCache) |
||
261 | ->setFilename(array('no-cache', 'nc')); |
||
262 | $img->injectDependency("fastTrackCache", $ftc); |
||
263 | |||
264 | |||
265 | |||
266 | /** |
||
267 | * Load and output images from fast track cache, if items are available |
||
268 | * in cache. |
||
269 | */ |
||
270 | if ($useCache && $allowFastTrackCache) { |
||
271 | if (CIMAGE_DEBUG) { |
||
272 | trace("img.php fast track cache enabled and used"); |
||
273 | } |
||
274 | $ftc->output(); |
||
275 | } |
||
276 | |||
277 | |||
278 | |||
279 | /** |
||
280 | * Allow or disallow remote download of images from other servers. |
||
281 | * Passwords apply if used. |
||
282 | * |
||
283 | */ |
||
284 | $allowRemote = getConfig('remote_allow', false); |
||
285 | |||
286 | if ($allowRemote && $passwordMatch !== false) { |
||
287 | $cacheRemote = $cache->getPathToSubdir("remote"); |
||
288 | |||
289 | $pattern = getConfig('remote_pattern', null); |
||
290 | $img->setRemoteDownload($allowRemote, $cacheRemote, $pattern); |
||
291 | |||
292 | $whitelist = getConfig('remote_whitelist', null); |
||
293 | $img->setRemoteHostWhitelist($whitelist); |
||
294 | } |
||
295 | |||
296 | |||
297 | |||
298 | /** |
||
299 | * shortcut, sc - extend arguments with a constant value, defined |
||
300 | * in config-file. |
||
301 | */ |
||
302 | $shortcut = get(array('shortcut', 'sc'), null); |
||
303 | $shortcutConfig = getConfig('shortcut', array( |
||
304 | 'sepia' => "&f=grayscale&f0=brightness,-10&f1=contrast,-20&f2=colorize,120,60,0,0&sharpen", |
||
305 | )); |
||
306 | |||
307 | verbose("shortcut = $shortcut"); |
||
308 | |||
309 | if (isset($shortcut) |
||
310 | && isset($shortcutConfig[$shortcut])) { |
||
311 | |||
312 | parse_str($shortcutConfig[$shortcut], $get); |
||
313 | verbose("shortcut-constant = {$shortcutConfig[$shortcut]}"); |
||
314 | $_GET = array_merge($_GET, $get); |
||
315 | } |
||
316 | |||
317 | |||
318 | |||
319 | /** |
||
320 | * src - the source image file. |
||
321 | */ |
||
322 | $srcImage = urldecode(get('src')) |
||
323 | or errorPage('Must set src-attribute.', 404); |
||
324 | |||
325 | // Get settings for src-alt as backup image |
||
326 | $srcAltImage = urldecode(get('src-alt', null)); |
||
327 | $srcAltConfig = getConfig('src_alt', null); |
||
328 | if (empty($srcAltImage)) { |
||
329 | $srcAltImage = $srcAltConfig; |
||
330 | } |
||
331 | |||
332 | // Check for valid/invalid characters |
||
333 | $imagePath = getConfig('image_path', __DIR__ . '/img/'); |
||
334 | $imagePathConstraint = getConfig('image_path_constraint', true); |
||
335 | $validFilename = getConfig('valid_filename', '#^[a-z0-9A-Z-/_ \.:]+$#'); |
||
336 | |||
337 | // Source is remote |
||
338 | $remoteSource = false; |
||
339 | |||
340 | // Dummy image feature |
||
341 | $dummyEnabled = getConfig('dummy_enabled', true); |
||
342 | $dummyFilename = getConfig('dummy_filename', 'dummy'); |
||
343 | $dummyImage = false; |
||
344 | |||
345 | preg_match($validFilename, $srcImage) |
||
346 | or errorPage('Source filename contains invalid characters.', 404); |
||
347 | |||
348 | if ($dummyEnabled && $srcImage === $dummyFilename) { |
||
349 | |||
350 | // Prepare to create a dummy image and use it as the source image. |
||
351 | $dummyImage = true; |
||
352 | |||
353 | } elseif ($allowRemote && $img->isRemoteSource($srcImage)) { |
||
354 | |||
355 | // If source is a remote file, ignore local file checks. |
||
356 | $remoteSource = true; |
||
357 | |||
358 | } else { |
||
359 | |||
360 | // Check if file exists on disk or try using src-alt |
||
361 | $pathToImage = realpath($imagePath . $srcImage); |
||
362 | |||
363 | if (!is_file($pathToImage) && !empty($srcAltImage)) { |
||
364 | // Try using the src-alt instead |
||
365 | $srcImage = $srcAltImage; |
||
366 | $pathToImage = realpath($imagePath . $srcImage); |
||
367 | |||
368 | preg_match($validFilename, $srcImage) |
||
369 | or errorPage('Source (alt) filename contains invalid characters.', 404); |
||
370 | |||
371 | if ($dummyEnabled && $srcImage === $dummyFilename) { |
||
372 | // Check if src-alt is the dummy image |
||
373 | $dummyImage = true; |
||
374 | } |
||
375 | } |
||
376 | |||
377 | if (!$dummyImage) { |
||
378 | is_file($pathToImage) |
||
379 | or errorPage( |
||
380 | 'Source image is not a valid file, check the filename and that a |
||
381 | matching file exists on the filesystem.', |
||
382 | 404 |
||
383 | ); |
||
384 | } |
||
385 | } |
||
386 | |||
387 | if ($imagePathConstraint && !$dummyImage && !$remoteSource) { |
||
388 | // Check that the image is a file below the directory 'image_path'. |
||
389 | $imageDir = realpath($imagePath); |
||
390 | |||
391 | substr_compare($imageDir, $pathToImage, 0, strlen($imageDir)) == 0 |
||
392 | or errorPage( |
||
393 | 'Security constraint: Source image is not below the directory "image_path" |
||
394 | as specified in the config file img_config.php.', |
||
395 | 404 |
||
396 | ); |
||
397 | } |
||
398 | |||
399 | verbose("src = $srcImage"); |
||
400 | |||
401 | |||
402 | |||
403 | /** |
||
404 | * Manage size constants from config file, use constants to replace values |
||
405 | * for width and height. |
||
406 | */ |
||
407 | $sizeConstant = getConfig('size_constant', function () { |
||
408 | |||
409 | // Set sizes to map constant to value, easier to use with width or height |
||
410 | $sizes = array( |
||
411 | 'w1' => 613, |
||
412 | 'w2' => 630, |
||
413 | ); |
||
414 | |||
415 | // Add grid column width, useful for use as predefined size for width (or height). |
||
416 | $gridColumnWidth = 30; |
||
417 | $gridGutterWidth = 10; |
||
418 | $gridColumns = 24; |
||
419 | |||
420 | for ($i = 1; $i <= $gridColumns; $i++) { |
||
421 | $sizes['c' . $i] = ($gridColumnWidth + $gridGutterWidth) * $i - $gridGutterWidth; |
||
422 | } |
||
423 | |||
424 | return $sizes; |
||
425 | }); |
||
426 | |||
427 | $sizes = call_user_func($sizeConstant); |
||
428 | |||
429 | |||
430 | |||
431 | /** |
||
432 | * width, w - set target width, affecting the resulting image width, height and resize options |
||
433 | */ |
||
434 | $newWidth = get(array('width', 'w')); |
||
435 | $maxWidth = getConfig('max_width', 2000); |
||
436 | |||
437 | // Check to replace predefined size |
||
438 | if (isset($sizes[$newWidth])) { |
||
439 | $newWidth = $sizes[$newWidth]; |
||
440 | } |
||
441 | |||
442 | // Support width as % of original width |
||
443 | if ($newWidth && $newWidth[strlen($newWidth)-1] == '%') { |
||
444 | is_numeric(substr($newWidth, 0, -1)) |
||
445 | or errorPage('Width % not numeric.', 404); |
||
446 | } else { |
||
447 | is_null($newWidth) |
||
448 | or ($newWidth > 10 && $newWidth <= $maxWidth) |
||
449 | or errorPage('Width out of range.', 404); |
||
450 | } |
||
451 | |||
452 | verbose("new width = $newWidth"); |
||
453 | |||
454 | |||
455 | |||
456 | /** |
||
457 | * height, h - set target height, affecting the resulting image width, height and resize options |
||
458 | */ |
||
459 | $newHeight = get(array('height', 'h')); |
||
460 | $maxHeight = getConfig('max_height', 2000); |
||
461 | |||
462 | // Check to replace predefined size |
||
463 | if (isset($sizes[$newHeight])) { |
||
464 | $newHeight = $sizes[$newHeight]; |
||
465 | } |
||
466 | |||
467 | // height |
||
468 | if ($newHeight && $newHeight[strlen($newHeight)-1] == '%') { |
||
469 | is_numeric(substr($newHeight, 0, -1)) |
||
470 | or errorPage('Height % out of range.', 404); |
||
471 | } else { |
||
472 | is_null($newHeight) |
||
473 | or ($newHeight > 10 && $newHeight <= $maxHeight) |
||
474 | or errorPage('Height out of range.', 404); |
||
475 | } |
||
476 | |||
477 | verbose("new height = $newHeight"); |
||
478 | |||
479 | |||
480 | |||
481 | /** |
||
482 | * aspect-ratio, ar - affecting the resulting image width, height and resize options |
||
483 | */ |
||
484 | $aspectRatio = get(array('aspect-ratio', 'ar')); |
||
485 | $aspectRatioConstant = getConfig('aspect_ratio_constant', function () { |
||
486 | return array( |
||
487 | '3:1' => 3/1, |
||
488 | '3:2' => 3/2, |
||
489 | '4:3' => 4/3, |
||
490 | '8:5' => 8/5, |
||
491 | '16:10' => 16/10, |
||
492 | '16:9' => 16/9, |
||
493 | 'golden' => 1.618, |
||
494 | ); |
||
495 | }); |
||
496 | |||
497 | // Check to replace predefined aspect ratio |
||
498 | $aspectRatios = call_user_func($aspectRatioConstant); |
||
499 | $negateAspectRatio = ($aspectRatio && $aspectRatio[0] == '!') ? true : false; |
||
500 | $aspectRatio = $negateAspectRatio ? substr($aspectRatio, 1) : $aspectRatio; |
||
501 | |||
502 | if (isset($aspectRatios[$aspectRatio])) { |
||
503 | $aspectRatio = $aspectRatios[$aspectRatio]; |
||
504 | } |
||
505 | |||
506 | if ($negateAspectRatio) { |
||
507 | $aspectRatio = 1 / $aspectRatio; |
||
508 | } |
||
509 | |||
510 | is_null($aspectRatio) |
||
511 | or is_numeric($aspectRatio) |
||
512 | or errorPage('Aspect ratio out of range', 404); |
||
513 | |||
514 | verbose("aspect ratio = $aspectRatio"); |
||
515 | |||
516 | |||
517 | |||
518 | /** |
||
519 | * crop-to-fit, cf - affecting the resulting image width, height and resize options |
||
520 | */ |
||
521 | $cropToFit = getDefined(array('crop-to-fit', 'cf'), true, false); |
||
522 | |||
523 | verbose("crop to fit = $cropToFit"); |
||
524 | |||
525 | |||
526 | |||
527 | /** |
||
528 | * Set default background color from config file. |
||
529 | */ |
||
530 | $backgroundColor = getConfig('background_color', null); |
||
531 | |||
532 | if ($backgroundColor) { |
||
533 | $img->setDefaultBackgroundColor($backgroundColor); |
||
534 | verbose("Using default background_color = $backgroundColor"); |
||
535 | } |
||
536 | |||
537 | |||
538 | |||
539 | /** |
||
540 | * bgColor - Default background color to use |
||
541 | */ |
||
542 | $bgColor = get(array('bgColor', 'bg-color', 'bgc'), null); |
||
543 | |||
544 | verbose("bgColor = $bgColor"); |
||
545 | |||
546 | |||
547 | |||
548 | /** |
||
549 | * Do or do not resample image when resizing. |
||
550 | */ |
||
551 | $resizeStrategy = getDefined(array('no-resample'), true, false); |
||
552 | |||
553 | if ($resizeStrategy) { |
||
554 | $img->setCopyResizeStrategy($img::RESIZE); |
||
555 | verbose("Setting = Resize instead of resample"); |
||
556 | } |
||
557 | |||
558 | |||
559 | |||
560 | |||
561 | /** |
||
562 | * fill-to-fit, ff - affecting the resulting image width, height and resize options |
||
563 | */ |
||
564 | $fillToFit = get(array('fill-to-fit', 'ff'), null); |
||
565 | |||
566 | verbose("fill-to-fit = $fillToFit"); |
||
567 | |||
568 | if ($fillToFit !== null) { |
||
569 | |||
570 | if (!empty($fillToFit)) { |
||
571 | $bgColor = $fillToFit; |
||
572 | verbose("fillToFit changed bgColor to = $bgColor"); |
||
573 | } |
||
574 | |||
575 | $fillToFit = true; |
||
576 | verbose("fill-to-fit (fixed) = $fillToFit"); |
||
577 | } |
||
578 | |||
579 | |||
580 | |||
581 | /** |
||
582 | * no-ratio, nr, stretch - affecting the resulting image width, height and resize options |
||
583 | */ |
||
584 | $keepRatio = getDefined(array('no-ratio', 'nr', 'stretch'), false, true); |
||
585 | |||
586 | verbose("keep ratio = $keepRatio"); |
||
587 | |||
588 | |||
589 | |||
590 | /** |
||
591 | * crop, c - affecting the resulting image width, height and resize options |
||
592 | */ |
||
593 | $crop = get(array('crop', 'c')); |
||
594 | |||
595 | verbose("crop = $crop"); |
||
596 | |||
597 | |||
598 | |||
599 | /** |
||
600 | * area, a - affecting the resulting image width, height and resize options |
||
601 | */ |
||
602 | $area = get(array('area', 'a')); |
||
603 | |||
604 | verbose("area = $area"); |
||
605 | |||
606 | |||
607 | |||
608 | /** |
||
609 | * skip-original, so - skip the original image and always process a new image |
||
610 | */ |
||
611 | $useOriginal = getDefined(array('skip-original', 'so'), false, true); |
||
612 | $useOriginalDefault = getConfig('skip_original', false); |
||
613 | |||
614 | if ($useOriginalDefault === true) { |
||
615 | verbose("skip original is default ON"); |
||
616 | $useOriginal = false; |
||
617 | } |
||
618 | |||
619 | verbose("use original = $useOriginal"); |
||
620 | |||
621 | |||
622 | |||
623 | /** |
||
624 | * quality, q - set level of quality for jpeg images |
||
625 | */ |
||
626 | $quality = get(array('quality', 'q')); |
||
627 | $qualityDefault = getConfig('jpg_quality', null); |
||
628 | |||
629 | is_null($quality) |
||
630 | or ($quality > 0 and $quality <= 100) |
||
631 | or errorPage('Quality out of range', 404); |
||
632 | |||
633 | if (is_null($quality) && !is_null($qualityDefault)) { |
||
634 | $quality = $qualityDefault; |
||
635 | } |
||
636 | |||
637 | verbose("quality = $quality"); |
||
638 | |||
639 | |||
640 | |||
641 | /** |
||
642 | * compress, co - what strategy to use when compressing png images |
||
643 | */ |
||
644 | $compress = get(array('compress', 'co')); |
||
645 | $compressDefault = getConfig('png_compression', null); |
||
646 | |||
647 | is_null($compress) |
||
648 | or ($compress > 0 and $compress <= 9) |
||
649 | or errorPage('Compress out of range', 404); |
||
650 | |||
651 | if (is_null($compress) && !is_null($compressDefault)) { |
||
652 | $compress = $compressDefault; |
||
653 | } |
||
654 | |||
655 | verbose("compress = $compress"); |
||
656 | |||
657 | |||
658 | |||
659 | /** |
||
660 | * save-as, sa - what type of image to save |
||
661 | */ |
||
662 | $saveAs = get(array('save-as', 'sa')); |
||
663 | |||
664 | verbose("save as = $saveAs"); |
||
665 | |||
666 | |||
667 | |||
668 | /** |
||
669 | * scale, s - Processing option, scale up or down the image prior actual resize |
||
670 | */ |
||
671 | $scale = get(array('scale', 's')); |
||
672 | |||
673 | is_null($scale) |
||
674 | or ($scale >= 0 and $scale <= 400) |
||
675 | or errorPage('Scale out of range', 404); |
||
676 | |||
677 | verbose("scale = $scale"); |
||
678 | |||
679 | |||
680 | |||
681 | /** |
||
682 | * palette, p - Processing option, create a palette version of the image |
||
683 | */ |
||
684 | $palette = getDefined(array('palette', 'p'), true, false); |
||
685 | |||
686 | verbose("palette = $palette"); |
||
687 | |||
688 | |||
689 | |||
690 | /** |
||
691 | * sharpen - Processing option, post filter for sharpen effect |
||
692 | */ |
||
693 | $sharpen = getDefined('sharpen', true, null); |
||
694 | |||
695 | verbose("sharpen = $sharpen"); |
||
696 | |||
697 | |||
698 | |||
699 | /** |
||
700 | * emboss - Processing option, post filter for emboss effect |
||
701 | */ |
||
702 | $emboss = getDefined('emboss', true, null); |
||
703 | |||
704 | verbose("emboss = $emboss"); |
||
705 | |||
706 | |||
707 | |||
708 | /** |
||
709 | * blur - Processing option, post filter for blur effect |
||
710 | */ |
||
711 | $blur = getDefined('blur', true, null); |
||
712 | |||
713 | verbose("blur = $blur"); |
||
714 | |||
715 | |||
716 | |||
717 | /** |
||
718 | * rotateBefore - Rotate the image with an angle, before processing |
||
719 | */ |
||
720 | $rotateBefore = get(array('rotateBefore', 'rotate-before', 'rb')); |
||
721 | |||
722 | is_null($rotateBefore) |
||
723 | or ($rotateBefore >= -360 and $rotateBefore <= 360) |
||
724 | or errorPage('RotateBefore out of range', 404); |
||
725 | |||
726 | verbose("rotateBefore = $rotateBefore"); |
||
727 | |||
728 | |||
729 | |||
730 | /** |
||
731 | * rotateAfter - Rotate the image with an angle, before processing |
||
732 | */ |
||
733 | $rotateAfter = get(array('rotateAfter', 'rotate-after', 'ra', 'rotate', 'r')); |
||
734 | |||
735 | is_null($rotateAfter) |
||
736 | or ($rotateAfter >= -360 and $rotateAfter <= 360) |
||
737 | or errorPage('RotateBefore out of range', 404); |
||
738 | |||
739 | verbose("rotateAfter = $rotateAfter"); |
||
740 | |||
741 | |||
742 | |||
743 | /** |
||
744 | * autoRotate - Auto rotate based on EXIF information |
||
745 | */ |
||
746 | $autoRotate = getDefined(array('autoRotate', 'auto-rotate', 'aro'), true, false); |
||
747 | |||
748 | verbose("autoRotate = $autoRotate"); |
||
749 | |||
750 | |||
751 | |||
752 | /** |
||
753 | * filter, f, f0-f9 - Processing option, post filter for various effects using imagefilter() |
||
754 | */ |
||
755 | $filters = array(); |
||
756 | $filter = get(array('filter', 'f')); |
||
757 | if ($filter) { |
||
758 | $filters[] = $filter; |
||
759 | } |
||
760 | |||
761 | for ($i = 0; $i < 10; $i++) { |
||
762 | $filter = get(array("filter{$i}", "f{$i}")); |
||
763 | if ($filter) { |
||
764 | $filters[] = $filter; |
||
765 | } |
||
766 | } |
||
767 | |||
768 | verbose("filters = " . print_r($filters, 1)); |
||
769 | |||
770 | |||
771 | |||
772 | /** |
||
773 | * json - output the image as a JSON object with details on the image. |
||
774 | * ascii - output the image as ASCII art. |
||
775 | */ |
||
776 | $outputFormat = getDefined('json', 'json', null); |
||
777 | $outputFormat = getDefined('ascii', 'ascii', $outputFormat); |
||
778 | |||
779 | verbose("outputformat = $outputFormat"); |
||
780 | |||
781 | if ($outputFormat == 'ascii') { |
||
782 | $defaultOptions = getConfig( |
||
783 | 'ascii-options', |
||
784 | array( |
||
785 | "characterSet" => 'two', |
||
786 | "scale" => 14, |
||
787 | "luminanceStrategy" => 3, |
||
788 | "customCharacterSet" => null, |
||
789 | ) |
||
790 | ); |
||
791 | $options = get('ascii'); |
||
792 | $options = explode(',', $options); |
||
793 | |||
794 | if (isset($options[0]) && !empty($options[0])) { |
||
795 | $defaultOptions['characterSet'] = $options[0]; |
||
796 | } |
||
797 | |||
798 | if (isset($options[1]) && !empty($options[1])) { |
||
799 | $defaultOptions['scale'] = $options[1]; |
||
800 | } |
||
801 | |||
802 | if (isset($options[2]) && !empty($options[2])) { |
||
803 | $defaultOptions['luminanceStrategy'] = $options[2]; |
||
804 | } |
||
805 | |||
806 | if (count($options) > 3) { |
||
807 | // Last option is custom character string |
||
808 | unset($options[0]); |
||
809 | unset($options[1]); |
||
810 | unset($options[2]); |
||
811 | $characterString = implode($options); |
||
812 | $defaultOptions['customCharacterSet'] = $characterString; |
||
813 | } |
||
814 | |||
815 | $img->setAsciiOptions($defaultOptions); |
||
816 | } |
||
817 | |||
818 | |||
819 | |||
820 | |||
821 | /** |
||
822 | * dpr - change to get larger image to easier support larger dpr, such as retina. |
||
823 | */ |
||
824 | $dpr = get(array('ppi', 'dpr', 'device-pixel-ratio'), 1); |
||
825 | |||
826 | verbose("dpr = $dpr"); |
||
827 | |||
828 | |||
829 | |||
830 | /** |
||
831 | * convolve - image convolution as in http://php.net/manual/en/function.imageconvolution.php |
||
832 | */ |
||
833 | $convolve = get('convolve', null); |
||
834 | $convolutionConstant = getConfig('convolution_constant', array()); |
||
835 | |||
836 | // Check if the convolve is matching an existing constant |
||
837 | if ($convolve && isset($convolutionConstant)) { |
||
838 | $img->addConvolveExpressions($convolutionConstant); |
||
839 | verbose("convolve constant = " . print_r($convolutionConstant, 1)); |
||
840 | } |
||
841 | |||
842 | verbose("convolve = " . print_r($convolve, 1)); |
||
843 | |||
844 | |||
845 | |||
846 | /** |
||
847 | * no-upscale, nu - Do not upscale smaller image to larger dimension. |
||
848 | */ |
||
849 | $upscale = getDefined(array('no-upscale', 'nu'), false, true); |
||
850 | |||
851 | verbose("upscale = $upscale"); |
||
852 | |||
853 | |||
854 | |||
855 | /** |
||
856 | * Get details for post processing |
||
857 | */ |
||
858 | $postProcessing = getConfig('postprocessing', array( |
||
859 | 'png_lossy' => false, |
||
860 | 'png_lossy_cmd' => '/usr/local/bin/pngquant --force --output', |
||
861 | |||
862 | 'png_filter' => false, |
||
863 | 'png_filter_cmd' => '/usr/local/bin/optipng -q', |
||
864 | |||
865 | 'png_deflate' => false, |
||
866 | 'png_deflate_cmd' => '/usr/local/bin/pngout -q', |
||
867 | |||
868 | 'jpeg_optimize' => false, |
||
869 | 'jpeg_optimize_cmd' => '/usr/local/bin/jpegtran -copy none -optimize', |
||
870 | )); |
||
871 | |||
872 | |||
873 | |||
874 | /** |
||
875 | * lossy - Do lossy postprocessing, if available. |
||
876 | */ |
||
877 | $lossy = getDefined(array('lossy'), true, null); |
||
878 | |||
879 | verbose("lossy = $lossy"); |
||
880 | |||
881 | |||
882 | |||
883 | /** |
||
884 | * alias - Save resulting image to another alias name. |
||
885 | * Password always apply, must be defined. |
||
886 | */ |
||
887 | $alias = get('alias', null); |
||
888 | $aliasPath = getConfig('alias_path', null); |
||
889 | $validAliasname = getConfig('valid_aliasname', '#^[a-z0-9A-Z-_]+$#'); |
||
890 | $aliasTarget = null; |
||
891 | |||
892 | if ($alias && $aliasPath && $passwordMatch) { |
||
893 | |||
894 | $aliasTarget = $aliasPath . $alias; |
||
895 | $useCache = false; |
||
896 | |||
897 | is_writable($aliasPath) |
||
898 | or errorPage("Directory for alias is not writable.", 403); |
||
899 | |||
900 | preg_match($validAliasname, $alias) |
||
901 | or errorPage('Filename for alias contains invalid characters. Do not add extension.', 404); |
||
902 | |||
903 | } elseif ($alias) { |
||
904 | errorPage('Alias is not enabled in the config file or password not matching.', 403); |
||
905 | } |
||
906 | |||
907 | verbose("alias = $alias"); |
||
908 | |||
909 | |||
910 | |||
911 | /** |
||
912 | * Add cache control HTTP header. |
||
913 | */ |
||
914 | $cacheControl = getConfig('cache_control', null); |
||
915 | |||
916 | if ($cacheControl) { |
||
917 | verbose("cacheControl = $cacheControl"); |
||
918 | $img->addHTTPHeader("Cache-Control", $cacheControl); |
||
919 | } |
||
920 | |||
921 | |||
922 | |||
923 | /** |
||
924 | * interlace - Enable configuration for interlaced progressive JPEG images. |
||
925 | */ |
||
926 | $interlaceConfig = getConfig('interlace', null); |
||
927 | $interlaceValue = getValue('interlace', null); |
||
928 | $interlaceDefined = getDefined('interlace', true, null); |
||
929 | $interlace = $interlaceValue ?? $interlaceDefined ?? $interlaceConfig; |
||
930 | verbose("interlace (configfile) = ", $interlaceConfig); |
||
931 | verbose("interlace = ", $interlace); |
||
932 | |||
933 | |||
934 | |||
935 | /** |
||
936 | * Prepare a dummy image and use it as source image. |
||
937 | */ |
||
938 | if ($dummyImage === true) { |
||
939 | $dummyDir = $cache->getPathToSubdir("dummy"); |
||
940 | |||
941 | $img->setSaveFolder($dummyDir) |
||
942 | ->setSource($dummyFilename, $dummyDir) |
||
943 | ->setOptions( |
||
944 | array( |
||
945 | 'newWidth' => $newWidth, |
||
946 | 'newHeight' => $newHeight, |
||
947 | 'bgColor' => $bgColor, |
||
948 | ) |
||
949 | ) |
||
950 | ->setJpegQuality($quality) |
||
951 | ->setPngCompression($compress) |
||
952 | ->createDummyImage() |
||
953 | ->generateFilename(null, false) |
||
954 | ->save(null, null, false); |
||
955 | |||
956 | $srcImage = $img->getTarget(); |
||
957 | $imagePath = null; |
||
958 | |||
959 | verbose("src (updated) = $srcImage"); |
||
960 | } |
||
961 | |||
962 | |||
963 | |||
964 | /** |
||
965 | * Prepare a sRGB version of the image and use it as source image. |
||
966 | */ |
||
967 | $srgbDefault = getConfig('srgb_default', false); |
||
968 | $srgbColorProfile = getConfig('srgb_colorprofile', __DIR__ . '/../icc/sRGB_IEC61966-2-1_black_scaled.icc'); |
||
969 | $srgb = getDefined('srgb', true, null); |
||
970 | |||
971 | if ($srgb || $srgbDefault) { |
||
972 | |||
973 | $filename = $img->convert2sRGBColorSpace( |
||
974 | $srcImage, |
||
975 | $imagePath, |
||
976 | $cache->getPathToSubdir("srgb"), |
||
977 | $srgbColorProfile, |
||
978 | $useCache |
||
979 | ); |
||
980 | |||
981 | if ($filename) { |
||
982 | $srcImage = $img->getTarget(); |
||
983 | $imagePath = null; |
||
984 | verbose("srgb conversion and saved to cache = $srcImage"); |
||
985 | } else { |
||
986 | verbose("srgb not op"); |
||
987 | } |
||
988 | } |
||
989 | |||
990 | |||
991 | |||
992 | /** |
||
993 | * Display status |
||
994 | */ |
||
995 | if ($status) { |
||
996 | $text = "img.php version = " . CIMAGE_VERSION . "\n"; |
||
997 | $text .= "PHP version = " . PHP_VERSION . "\n"; |
||
998 | $text .= "Running on: " . $_SERVER['SERVER_SOFTWARE'] . "\n"; |
||
999 | $text .= "Allow remote images = $allowRemote\n"; |
||
1000 | |||
1001 | $res = $cache->getStatusOfSubdir(""); |
||
1002 | $text .= "Cache $res\n"; |
||
1003 | |||
1004 | $res = $cache->getStatusOfSubdir("remote"); |
||
1005 | $text .= "Cache remote $res\n"; |
||
1006 | |||
1007 | $res = $cache->getStatusOfSubdir("dummy"); |
||
1008 | $text .= "Cache dummy $res\n"; |
||
1009 | |||
1010 | $res = $cache->getStatusOfSubdir("srgb"); |
||
1011 | $text .= "Cache srgb $res\n"; |
||
1012 | |||
1013 | $res = $cache->getStatusOfSubdir($fastTrackCache); |
||
1014 | $text .= "Cache fasttrack $res\n"; |
||
1015 | |||
1016 | $text .= "Alias path writable = " . is_writable($aliasPath) . "\n"; |
||
1017 | |||
1018 | $no = extension_loaded('exif') ? null : 'NOT'; |
||
1019 | $text .= "Extension exif is $no loaded.<br>"; |
||
1020 | |||
1021 | $no = extension_loaded('curl') ? null : 'NOT'; |
||
1022 | $text .= "Extension curl is $no loaded.<br>"; |
||
1023 | |||
1024 | $no = extension_loaded('imagick') ? null : 'NOT'; |
||
1025 | $text .= "Extension imagick is $no loaded.<br>"; |
||
1026 | |||
1027 | $no = extension_loaded('gd') ? null : 'NOT'; |
||
1028 | $text .= "Extension gd is $no loaded.<br>"; |
||
1029 | |||
1030 | $text .= checkExternalCommand("PNG LOSSY", $postProcessing["png_lossy"], $postProcessing["png_lossy_cmd"]); |
||
1031 | $text .= checkExternalCommand("PNG FILTER", $postProcessing["png_filter"], $postProcessing["png_filter_cmd"]); |
||
1032 | $text .= checkExternalCommand("PNG DEFLATE", $postProcessing["png_deflate"], $postProcessing["png_deflate_cmd"]); |
||
1033 | $text .= checkExternalCommand("JPEG OPTIMIZE", $postProcessing["jpeg_optimize"], $postProcessing["jpeg_optimize_cmd"]); |
||
1034 | |||
1035 | if (!$no) { |
||
1036 | $text .= print_r(gd_info(), 1); |
||
1037 | } |
||
1038 | |||
1039 | echo <<<EOD |
||
1040 | <!doctype html> |
||
1041 | <html lang=en> |
||
1042 | <meta charset=utf-8> |
||
1043 | <title>CImage status</title> |
||
1044 | <pre>$text</pre> |
||
1045 | EOD; |
||
1046 | exit; |
||
1047 | } |
||
1048 | |||
1049 | |||
1050 | |||
1051 | /** |
||
1052 | * Log verbose details to file |
||
1053 | */ |
||
1054 | if ($verboseFile) { |
||
1055 | $img->setVerboseToFile("$cachePath/log.txt"); |
||
1056 | } |
||
1057 | |||
1058 | |||
1059 | |||
1060 | /** |
||
1061 | * Hook after img.php configuration and before processing with CImage |
||
1062 | */ |
||
1063 | $hookBeforeCImage = getConfig('hook_before_CImage', null); |
||
1064 | |||
1065 | if (is_callable($hookBeforeCImage)) { |
||
1066 | verbose("hookBeforeCImage activated"); |
||
1067 | |||
1068 | $allConfig = $hookBeforeCImage($img, array( |
||
1069 | // Options for calculate dimensions |
||
1070 | 'newWidth' => $newWidth, |
||
1071 | 'newHeight' => $newHeight, |
||
1072 | 'aspectRatio' => $aspectRatio, |
||
1073 | 'keepRatio' => $keepRatio, |
||
1074 | 'cropToFit' => $cropToFit, |
||
1075 | 'fillToFit' => $fillToFit, |
||
1076 | 'crop' => $crop, |
||
1077 | 'area' => $area, |
||
1078 | 'upscale' => $upscale, |
||
1079 | |||
1080 | // Pre-processing, before resizing is done |
||
1081 | 'scale' => $scale, |
||
1082 | 'rotateBefore' => $rotateBefore, |
||
1083 | 'autoRotate' => $autoRotate, |
||
1084 | |||
1085 | // General processing options |
||
1086 | 'bgColor' => $bgColor, |
||
1087 | |||
1088 | // Post-processing, after resizing is done |
||
1089 | 'palette' => $palette, |
||
1090 | 'filters' => $filters, |
||
1091 | 'sharpen' => $sharpen, |
||
1092 | 'emboss' => $emboss, |
||
1093 | 'blur' => $blur, |
||
1094 | 'convolve' => $convolve, |
||
1095 | 'rotateAfter' => $rotateAfter, |
||
1096 | 'interlace' => $interlace, |
||
1097 | |||
1098 | // Output format |
||
1099 | 'outputFormat' => $outputFormat, |
||
1100 | 'dpr' => $dpr, |
||
1101 | |||
1102 | // Other |
||
1103 | 'postProcessing' => $postProcessing, |
||
1104 | 'lossy' => $lossy, |
||
1105 | )); |
||
1106 | verbose(print_r($allConfig, 1)); |
||
1107 | extract($allConfig); |
||
1108 | } |
||
1109 | |||
1110 | |||
1111 | |||
1112 | /** |
||
1113 | * Display image if verbose mode |
||
1114 | */ |
||
1115 | if ($verbose) { |
||
1116 | $query = array(); |
||
1117 | parse_str($_SERVER['QUERY_STRING'], $query); |
||
1118 | unset($query['verbose']); |
||
1119 | unset($query['v']); |
||
1120 | unset($query['nocache']); |
||
1121 | unset($query['nc']); |
||
1122 | unset($query['json']); |
||
1123 | $url1 = '?' . htmlentities(urldecode(http_build_query($query))); |
||
1124 | $url2 = '?' . urldecode(http_build_query($query)); |
||
1125 | echo <<<EOD |
||
1126 | <!doctype html> |
||
1127 | <html lang=en> |
||
1128 | <meta charset=utf-8> |
||
1129 | <title>CImage verbose output</title> |
||
1130 | <style>body{background-color: #ddd}</style> |
||
1131 | <a href=$url1><code>$url1</code></a><br> |
||
1132 | <img src='{$url1}' /> |
||
1133 | <pre id="json"></pre> |
||
1134 | <script src="https://code.jquery.com/jquery-2.1.1.min.js"></script> |
||
1135 | <script type="text/javascript"> |
||
1136 | window.getDetails = function (url, id) { |
||
1137 | $.getJSON(url, function(data) { |
||
1138 | element = document.getElementById(id); |
||
1139 | element.innerHTML = "filename: " + data.filename + "\\nmime type: " + data.mimeType + "\\ncolors: " + data.colors + "\\nsize: " + data.size + "\\nwidth: " + data.width + "\\nheigh: " + data.height + "\\naspect-ratio: " + data.aspectRatio + ( data.pngType ? "\\npng-type: " + data.pngType : ''); |
||
1140 | }); |
||
1141 | } |
||
1142 | </script> |
||
1143 | <script type="text/javascript">window.getDetails("{$url2}&json", "json")</script> |
||
1144 | EOD; |
||
1145 | } |
||
1146 | |||
1147 | |||
1148 | |||
1149 | /** |
||
1150 | * Load, process and output the image |
||
1151 | */ |
||
1152 | $img->log("Incoming arguments: " . print_r(verbose(), 1)) |
||
1153 | ->setSaveFolder($cachePath) |
||
1154 | ->useCache($useCache) |
||
1155 | ->setSource($srcImage, $imagePath) |
||
1156 | ->setOptions( |
||
1157 | array( |
||
1158 | // Options for calculate dimensions |
||
1159 | 'newWidth' => $newWidth, |
||
1160 | 'newHeight' => $newHeight, |
||
1161 | 'aspectRatio' => $aspectRatio, |
||
1162 | 'keepRatio' => $keepRatio, |
||
1163 | 'cropToFit' => $cropToFit, |
||
1164 | 'fillToFit' => $fillToFit, |
||
1165 | 'crop' => $crop, |
||
1166 | 'area' => $area, |
||
1167 | 'upscale' => $upscale, |
||
1168 | |||
1169 | // Pre-processing, before resizing is done |
||
1170 | 'scale' => $scale, |
||
1171 | 'rotateBefore' => $rotateBefore, |
||
1172 | 'autoRotate' => $autoRotate, |
||
1173 | |||
1174 | // General processing options |
||
1175 | 'bgColor' => $bgColor, |
||
1176 | |||
1177 | // Post-processing, after resizing is done |
||
1178 | 'palette' => $palette, |
||
1179 | 'filters' => $filters, |
||
1180 | 'sharpen' => $sharpen, |
||
1181 | 'emboss' => $emboss, |
||
1182 | 'blur' => $blur, |
||
1183 | 'convolve' => $convolve, |
||
1184 | 'rotateAfter' => $rotateAfter, |
||
1185 | 'interlace' => $interlace, |
||
1186 | |||
1187 | // Output format |
||
1188 | 'outputFormat' => $outputFormat, |
||
1189 | 'dpr' => $dpr, |
||
1190 | |||
1191 | // Postprocessing using external tools |
||
1192 | 'lossy' => $lossy, |
||
1193 | ) |
||
1194 | ) |
||
1195 | ->loadImageDetails() |
||
1196 | ->initDimensions() |
||
1197 | ->calculateNewWidthAndHeight() |
||
1198 | ->setSaveAsExtension($saveAs) |
||
1199 | ->setJpegQuality($quality) |
||
1200 | ->setPngCompression($compress) |
||
1201 | ->useOriginalIfPossible($useOriginal) |
||
1202 | ->generateFilename($cachePath) |
||
1203 | ->useCacheIfPossible($useCache) |
||
1204 | ->load() |
||
1205 | ->preResize() |
||
1206 | ->resize() |
||
1207 | ->postResize() |
||
1208 | ->setPostProcessingOptions($postProcessing) |
||
1209 | ->save() |
||
1210 | ->linkToCacheFile($aliasTarget) |
||
1211 | ->output(); |
||
1212 |
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
.