Upload.php 6.5 KB

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