3 # This program is in the public domain within the United States. Additionally,
4 # we waive copyright and related rights in the work worldwide through the CC0
5 # 1.0 Universal public domain dedication, which can be found at
6 # http://creativecommons.org/publicdomain/zero/1.0/
9 # This file contains functions to accept files being uplodad with the <input
10 # type="file" name="foo"> control.
16 # First, your <form> tag must contain this attribute:
17 # enctype="multipart/form-data"
19 # Second, you should indicate to the browser the maximum file size (in bytes)
20 # allowed for uploads with a hidden input field named MAX_FILE_SIZE. You can
21 # use the function upload_max_filesize() to get the maximum allowed size that
26 # <form action="foo.php" enctype="multipart/form-data" method="post">
27 # <input type="hidden" name="MAX_FILE_SIZE" value="2097152">
28 # <input type="file" name="photo">
29 # <input type="submit" name="save" value="Save">
36 # In the php code you can use either save_uploaded_file('photo',
37 # 'upload/dir/'); or save_uploaded_image('photo', 'upload/dir/'); The only
38 # difference being that save_uploaded_image() will convert gifs to PNGs.
40 # Both functions will generate a reasonable filename based on the filename
41 # passed from the browser (and on the mime-type if there's no extension) unless
42 # you specify a filename. See the comments above the function definitions below
45 # In a future version of save_uploaded_image(), when you specify a filename, it
46 # will check the image type of the uploaded image, and if it's different than
47 # the type you specified, it will convert the image for you.
50 $GLOBALS['mime_to_ext'] = array(
51 'text/plain' => 'txt',
52 'text/html' => 'html',
53 'image/jpeg' => 'jpg',
58 'application/pdf' => 'pdf'
61 $GLOBALS['ext_to_ext'] = array(
68 # return the upload_max_filesize in bytes
69 function upload_max_filesize() {
70 $max = ini_get('upload_max_filesize');
71 $postfix = strtolower(substr($max, -1));
73 return substr($max, 0, -1) * 1073741824;
74 } elseif($postfix == 'm') {
75 return substr($max, 0, -1) * 1048576;
76 } elseif ($postfix == 'k') {
77 return substr($max, 0, -1) * 1024;
84 # pass in the client's path that came from an html <input type="file"/> tag
86 # mime time used to generate extension ONLY IF it doesn't have one already.
87 function generate_filename($path, $mime = 'text/plain') {
89 $filename = strtolower($path);
91 # remove directories (unix, windows and mac paths)
92 $last = strrpos($filename, '/');
94 $last = strrpos($filename, '\\');
97 $last = strrpos($filename, ':');
100 $filename = substr($filename, $last + 1);
103 # replace symbols with underscores
104 $filename = preg_replace('|[^a-z0-9_.]|', '_', $filename);
107 if(strlen($filename > 80)) {
108 $filename = substr($filename, -80);
111 # remove dots from the beginning (no invisible files)
112 $filename = preg_replace('|^\.*|', '', $filename);
114 # make sure there's something before the extension
115 if ($filename == '') {
120 $last_dot = strrpos($filename, '.');
121 if($last_dot === false) {
123 if(isset($GLOBALS['mime_to_ext'][$mime])) {
124 $filename .= '.' . $GLOBALS['mime_to_ext'][$mime];
129 $basename = substr($filename, 0, $last_dot);
130 $ext = substr($filename, $last_dot + 1);
131 if(isset($GLOBALS['ext_to_ext'][$ext])) {
132 $ext .= $GLOBALS['ext_to_ext'][$ext];
134 $filename = $basename . '.' . $ext;
142 # Move uploaded file, and return the new filename.
144 # $key: Pass in the index into the $_FILES array (the name of the html input tag) and
145 # the path to the folder you'd like it saved to.
147 # $path: If path ends with a slash this function will generate a filename based
148 # on the client's name. If it ends with a period, the dot will be removed and
149 # the client's name appended. Otherwise $path will be used as the filename
150 # exactly as is, even if extensions differ between the client's name and $path.
152 # where user uploads "c:\foo\Bar baz.PDF" at <input name="in" type="file">
153 # save_uploaded_file('in', 'uploaded_pdfs/'); yeilds:
154 # "uploaded_pdfs/bar_baz.pdf"
155 # save_uploaded_file('in', 'uploaded_pdfs/prefix.'); yeilds:
156 # "uploaded_pdfs/prefixbar_baz.pdf"
157 # save_uploaded_file('in', 'uploaded_pdfs/qux.pdf'); yeilds:
158 # "uploaded_pdfs/qux.pdf"
159 function save_uploaded_file($key, $path) {
160 $end = substr($path, -1);
161 if($end == '.' || $end == '/') {
163 $path = substr($path, 0, -1);
165 $filename = $path . generate_filename($_FILES[$key]['name'], $_FILES[$key]['type']);
170 if(!move_uploaded_file($_FILES[$key]['tmp_name'], $filename)) {
177 # this function exists to deal with cases where binaries are installed in very
178 # standard places (like /usr/bin or /usr/local bin) and PHP's PATH environment
179 # variable is not set appropriately.
180 function path_to($prog, $or_die = true) {
181 $prog = preg_replace('|[^a-z0-9_.-]|i', '', $prog);
182 $prog = preg_replace('|^[-.]*|', '', $prog);
184 die('Invalid argument to path_to()');
187 if(!isset($GLOBALS["path_to_$prog"])) {
188 $ret = _path_to($prog, $or_die);
192 $GLOBALS["path_to_$prog"] = $ret;
195 return $GLOBALS["path_to_$prog"];
198 function _path_to($prog, $or_die) {
199 # relies on PHP's short-circuit mechanism
200 if(file_exists($path = "/usr/local/bin/$prog") ||
201 file_exists($path = "/usr/bin/$prog") ||
202 ($path = `which $prog` != '' && file_exists($path))) {
206 die("Failed to locate '$prog' executable.");
213 # returns new filename with .png extension
214 function gif_to_png($filename, $new_filename = 'just change extension') {
215 if($new_filename == 'just change extension') {
216 $new_filename = $filename;
217 $last_dot = strrpos($new_filename, '.');
218 if($last_dot !== false) {
219 $new_filename = substr($new_filename, 0, $last_dot);
221 $new_filename .= '.png';
224 imagemagick_convert($filename, $new_filename, "-colorspace sRGB", 'GIF to PNG conversion');
227 return $new_filename;
230 # make a thumbnail image.
232 # Thumbnail will have the same filename, except "_thumb" will be added right
233 # before the dot preceding the extension. so foo.png yields foo_thumb.png
235 # Thumbnail will retain aspect ratio, and be either $max_width wide or
236 # $max_height tall (or, if the aspect is just right, both)
237 function make_thumbnail($filename, $max_width = '70', $max_height = '70') {
238 $last_dot = strrpos($filename, '.');
239 if($last_dot === false) {
240 die("couldn't make thumbnail because filename has no extension.");
243 $thumb = substr($filename, 0, $last_dot);
245 $thumb .= substr($filename, $last_dot);
247 $max_width = format_int_70($max_width);
248 $height_width = format_int_70($height_width);
250 imagemagick_convert($filename, $thumb, "-geometry ${max_width}x$max_height", 'Thumbnail creation');
255 function exec_or_die($command, $doing_what) {
256 exec($command, $dummy, $ret);
258 $base = basename(preg_replace('| .*|', '', $command));
259 die("$doing_what failed. $base called exit($ret)");
263 # exec convert from imagemagick.
264 function imagemagick_convert($in_filename, $out_filename, $args, $doing_what = "Image conversion") {
265 $in = escapeshellarg($in_filename);
266 $out = escapeshellarg($out_filename);
267 $command = path_to('convert') . " $in $args $out";
269 exec_or_die($command, $doing_what);
272 # exec mogrify from imagemagick.
273 function imagemagick_mogrify($in_filename, $args, $doing_what = "Image conversion") {
274 $command = path_to('mogrify') . " $args " . escapeshellarg($in_filename);
276 exec_or_die($command, $doing_what);
279 function format_int_70($str) {
280 $str = preg_replace('|[^0-9]|', '', $str);
290 # The image will retain aspect ratio, and be either $max_width wide or
291 # $max_height tall (or, if the aspect is just right, both)
292 function resize_image($filename, $max_width = '70', $max_height = '70') {
293 $max_width = format_int_70($max_width);
294 $height_width = format_int_70($height_width);
296 imagimagick_mogrify($filename, "-geometry ${max_width}x$max_height");
299 # Argument: path to image file
301 # Return: string in the format WIDTHxHEIGHT, or boolean false
303 # Example: image_dimensions('uploads/foo.png'); ==> "124x58"
304 function image_dimensions($image) {
305 $identify = path_to('identify');
306 $command = "$identify -format '%wx%h' " . escapeshellarg($image);
307 $dimensions = rtrim(`$command`);
308 if($dimensions == '') {
315 # return an array of the width and height of the image passed.
316 # calls die() if this can't be done for any reason.
317 function image_w_h_or_die($filename) {
318 $wxh = image_dimensions($filename);
320 die("couldn't git image dimensions of $filename");
322 $wh = explode('x', $wxh);
323 if(count($wh) != 2) {
324 die("image $filename seems to have " . count($wh) . ' dimensions');
330 # Like save_uploaded_file() (above) except that it converts all images to PNG
331 # or JPEG, converts to sRGB colorspace, and optionally scales and/or creates a
332 # thumbnail. And, if $path ends with a period, the correct extension will be
335 # You are encouraged to use convert_uploaded_image() instead of this function,
336 # because it has a more useful return value.
338 # If the image_width and image_height parameters are above zero, then the image
339 # will be scaled (see below).
341 # If the thumb_width and thumb_height parameters are above zero, then a 2nd
342 # image will be created and scaled (see below) with the same name, except
343 # having "_thumb" added before the extension.
345 # Scaling: images are scaled (maintaining the aspect ratio) so they are as big
346 # as possible without either dimension being larger than what you specify.
348 # This function just returns the name of the main image. To get the dimensions
349 # and names, call convert_uploaded_image().
350 function save_uploaded_image($key, $path, $image_width = 0, $image_height = 0, $thumbnail_width = 0, $thumbnail_height = 0) {
351 $image_w_h_thumb_w_h = convert_uploaded_image($key, $path, $image_width, $image_height, $thumbnail_width, $thumbnail_height);
352 return preg_replace('| .*|', '', $image_w_h_thumb_w_h);
355 function ext_to_web_image_ext($in) {
356 if($in == 'png' || $in == 'gif') {
363 # this function is just like save_uploaded_image() above except that the return
364 # value is a string like "filename width height" or (if you specified both
365 # thumbnail dimensions) "image_filename image_width image_height thumb_filename
366 # thumb_width thumb_height"
370 # convert_uploaded_image('image', 'uploads/', 500, 500);
371 # might return: "uploads/foo.jpg 500 400"
372 # convert_uploaded_image('image', 'uploads/', 500, 500, 70, 70);
373 # might return: "uploads/foo.jpg 500 400 uploads/foo_thumb.jpg 70 56"
374 function convert_uploaded_image($key, $path, $image_width = 0, $image_height = 0, $thumb_width = 0, $thumb_height = 0) {
376 $tmp_filename = save_uploaded_file($key, $path . '__.');
377 $ext_rpos = strrpos($tmp_filename, '.');
378 if($ext_rpos === false) {
379 die('save_uploaded_file() gave us a filename with no extension.');
381 $tmp_base = substr($tmp_filename, 0, $ext_rpos);
382 $tmp_ext = substr($tmp_filename, $ext_rpos + 1);
383 if(substr($path, -1) == '/') {
384 $filename = $path . substr($tmp_base, strlen($path) + 2);
385 $filename .= '.' . ext_to_web_image_ext($tmp_ext);
386 } elseif(substr($path, -1) == '.') {
387 $filename = $path . ext_to_web_image_ext($tmp_ext);
392 $convert_params = '-colorspace sRGB -auto-orient';
393 if($image_width > 0 && $image_height > 0) {
394 $convert_params .= " -geometry ${image_width}x$image_height";
396 imagemagick_convert($tmp_filename, $filename, $convert_params);
397 unlink($tmp_filename);
398 list($w, $h) = image_w_h_or_die($filename);
399 $ret = "$filename $w $h";
400 if($thumb_width > 0 && $thumb_height > 0) {
401 $thumb_name = make_thumbnail($filename, $thumb_width, $thumb_height);
402 list($w, $h) = image_w_h_or_die($thumb_name);
403 $ret .= " $thumb_name $w $h";