Upload.php 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. <?php
  2. /**
  3. * Upload helper class for working with uploaded files and [Validation].
  4. *
  5. * $array = Validation::factory($_FILES);
  6. *
  7. * [!!] Remember to define your form with "enctype=multipart/form-data" or file
  8. * uploading will not work!
  9. *
  10. * The following configuration properties can be set:
  11. *
  12. * - [Upload::$remove_spaces]
  13. * - [Upload::$default_directory]
  14. *
  15. * @package Kohana
  16. * @category Helpers
  17. * @author Kohana Team
  18. * @copyright (c) Kohana Team
  19. * @license https://koseven.ga/LICENSE.md
  20. */
  21. class Kohana_Upload {
  22. /**
  23. * @var boolean remove spaces in uploaded files
  24. */
  25. public static $remove_spaces = TRUE;
  26. /**
  27. * @var string default upload directory
  28. */
  29. public static $default_directory = 'upload';
  30. /**
  31. * Save an uploaded file to a new location. If no filename is provided,
  32. * the original filename will be used, with a unique prefix added.
  33. *
  34. * This method should be used after validating the $_FILES array:
  35. *
  36. * if ($array->check())
  37. * {
  38. * // Upload is valid, save it
  39. * Upload::save($array['file']);
  40. * }
  41. *
  42. * @param array $file uploaded file data
  43. * @param string $filename new filename
  44. * @param string $directory new directory
  45. * @param integer $chmod chmod mask
  46. * @return string on success, full path to new file
  47. * @return FALSE on failure
  48. */
  49. public static function save(array $file, $filename = NULL, $directory = NULL, $chmod = 0644)
  50. {
  51. if ( ! isset($file['tmp_name']) OR ! is_uploaded_file($file['tmp_name']))
  52. {
  53. // Ignore corrupted uploads
  54. return FALSE;
  55. }
  56. if ($filename === NULL)
  57. {
  58. // Use the default filename, with a timestamp pre-pended
  59. $filename = uniqid().$file['name'];
  60. }
  61. if (Upload::$remove_spaces === TRUE)
  62. {
  63. // Remove spaces from the filename
  64. $filename = preg_replace('/\s+/u', '_', $filename);
  65. }
  66. if ($directory === NULL)
  67. {
  68. // Use the pre-configured upload directory
  69. $directory = Upload::$default_directory;
  70. }
  71. if ( ! is_dir($directory) OR ! is_writable(realpath($directory)))
  72. {
  73. throw new Kohana_Exception('Directory :dir must be writable',
  74. [':dir' => Debug::path($directory)]);
  75. }
  76. // Make the filename into a complete path
  77. $filename = realpath($directory).DIRECTORY_SEPARATOR.$filename;
  78. if (move_uploaded_file($file['tmp_name'], $filename))
  79. {
  80. if ($chmod !== FALSE)
  81. {
  82. // Set permissions on filename
  83. chmod($filename, $chmod);
  84. }
  85. // Return new file path
  86. return $filename;
  87. }
  88. return FALSE;
  89. }
  90. /**
  91. * Tests if upload data is valid, even if no file was uploaded. If you
  92. * _do_ require a file to be uploaded, add the [Upload::not_empty] rule
  93. * before this rule.
  94. *
  95. * $array->rule('file', 'Upload::valid')
  96. *
  97. * @param array $file $_FILES item
  98. * @return bool
  99. */
  100. public static function valid($file)
  101. {
  102. return (isset($file['error'])
  103. AND isset($file['name'])
  104. AND isset($file['type'])
  105. AND isset($file['tmp_name'])
  106. AND isset($file['size']));
  107. }
  108. /**
  109. * Tests if a successful upload has been made.
  110. *
  111. * $array->rule('file', 'Upload::not_empty');
  112. *
  113. * @param array $file $_FILES item
  114. * @return bool
  115. */
  116. public static function not_empty(array $file)
  117. {
  118. return (isset($file['error'])
  119. AND isset($file['tmp_name'])
  120. AND $file['error'] === UPLOAD_ERR_OK
  121. AND is_uploaded_file($file['tmp_name']));
  122. }
  123. /**
  124. * Test if an uploaded file is an allowed file type, by extension.
  125. *
  126. * $array->rule('file', 'Upload::type', array(':value', array('jpg', 'png', 'gif')));
  127. *
  128. * @param array $file $_FILES item
  129. * @param array $allowed allowed file extensions
  130. * @return bool
  131. */
  132. public static function type(array $file, array $allowed)
  133. {
  134. if ($file['error'] !== UPLOAD_ERR_OK)
  135. return TRUE;
  136. $ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
  137. return in_array($ext, $allowed);
  138. }
  139. /**
  140. * Validation rule to test if an uploaded file is allowed by file size.
  141. * File sizes are defined as: SB, where S is the size (1, 8.5, 300, etc.)
  142. * and B is the byte unit (K, MiB, GB, etc.). All valid byte units are
  143. * defined in Num::$byte_units
  144. *
  145. * $array->rule('file', 'Upload::size', array(':value', '1M'))
  146. * $array->rule('file', 'Upload::size', array(':value', '2.5KiB'))
  147. *
  148. * @param array $file $_FILES item
  149. * @param string $size maximum file size allowed
  150. * @return bool
  151. */
  152. public static function size(array $file, $size)
  153. {
  154. if ($file['error'] === UPLOAD_ERR_INI_SIZE)
  155. {
  156. // Upload is larger than PHP allowed size (upload_max_filesize)
  157. return FALSE;
  158. }
  159. if ($file['error'] !== UPLOAD_ERR_OK)
  160. {
  161. // The upload failed, no size to check
  162. return TRUE;
  163. }
  164. // Convert the provided size to bytes for comparison
  165. $size = Num::bytes($size);
  166. // Test that the file is under or equal to the max size
  167. return ($file['size'] <= $size);
  168. }
  169. /**
  170. * Validation rule to test if an upload is an image and, optionally, is the correct size.
  171. *
  172. * // The "image" file must be an image
  173. * $array->rule('image', 'Upload::image')
  174. *
  175. * // The "photo" file has a maximum size of 640x480 pixels
  176. * $array->rule('photo', 'Upload::image', array(':value', 640, 480));
  177. *
  178. * // The "image" file must be exactly 100x100 pixels
  179. * $array->rule('image', 'Upload::image', array(':value', 100, 100, TRUE));
  180. *
  181. *
  182. * @param array $file $_FILES item
  183. * @param integer $max_width maximum width of image
  184. * @param integer $max_height maximum height of image
  185. * @param boolean $exact match width and height exactly?
  186. * @return boolean
  187. */
  188. public static function image(array $file, $max_width = NULL, $max_height = NULL, $exact = FALSE)
  189. {
  190. if (Upload::not_empty($file))
  191. {
  192. try
  193. {
  194. // Get the width and height from the uploaded image
  195. list($width, $height) = getimagesize($file['tmp_name']);
  196. }
  197. catch (ErrorException $e)
  198. {
  199. // Ignore read errors
  200. }
  201. if (empty($width) OR empty($height))
  202. {
  203. // Cannot get image size, cannot validate
  204. return FALSE;
  205. }
  206. if ( ! $max_width)
  207. {
  208. // No limit, use the image width
  209. $max_width = $width;
  210. }
  211. if ( ! $max_height)
  212. {
  213. // No limit, use the image height
  214. $max_height = $height;
  215. }
  216. if ($exact)
  217. {
  218. // Check if dimensions match exactly
  219. return ($width === $max_width AND $height === $max_height);
  220. }
  221. else
  222. {
  223. // Check if size is within maximum dimensions
  224. return ($width <= $max_width AND $height <= $max_height);
  225. }
  226. }
  227. return FALSE;
  228. }
  229. }