PageRenderTime 45ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/system/classes/helpers/file.php

http://github.com/enormego/EightPHP
PHP | 367 lines | 290 code | 22 blank | 55 comment | 10 complexity | c8ad44db9f00e0a42391922ec30b3eaa MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. /**
  3. * File helper class.
  4. *
  5. * @package System
  6. * @subpackage Helpers
  7. * @author EightPHP Development Team
  8. * @copyright (c) 2009-2010 EightPHP
  9. * @license http://license.eightphp.com
  10. */
  11. class file_Core {
  12. // Location of Mime Magic DB
  13. const MAGIC_DB = '/usr/share/file/magic';
  14. // Dir push/pop stack
  15. static $dir_stack = array();
  16. /**
  17. * Moves into a new directory
  18. *
  19. * @param string directory to move into
  20. */
  21. public static function push_dir($dir) {
  22. array_push(self::$dir_stack, getcwd());
  23. chdir($dir);
  24. }
  25. /**
  26. * Pops back to previous directory
  27. */
  28. public static function pop_dir() {
  29. if(!arr::e(self::$dir_stack)) {
  30. $dir = array_pop(self::$dir_stack);
  31. chdir($dir);
  32. }
  33. }
  34. /**
  35. * Recursive version of php's native glob() method
  36. *
  37. * @param int the pattern passed to glob()
  38. * @param int the flags passed to glob()
  39. * @param string the path to scan
  40. * @return mixed an array of files in the given path matching the pattern.
  41. */
  42. public static function rglob($pattern='*', $flags = 0, $path='') {
  43. $paths = glob($path.'*', GLOB_MARK|GLOB_ONLYDIR|GLOB_NOSORT);
  44. $files = glob($path.$pattern, $flags);
  45. foreach ($paths as $path) {
  46. $files = array_merge($files, file::rglob($pattern, $flags, $path));
  47. }
  48. return $files;
  49. }
  50. /**
  51. * Attempt to get the mime type from a file. This method is horribly
  52. * unreliable, due to PHP being horribly unreliable when it comes to
  53. * determining the mime-type of a file.
  54. *
  55. * @param string filename
  56. * @return string|boolean mime-type: if found, false: if not found
  57. */
  58. public static function mime($filename) {
  59. // Make sure the file is readable
  60. if(!(is_file($filename) and is_readable($filename)))
  61. return NO;
  62. // Get the extension from the filename
  63. $extension = strtolower(substr(strrchr($filename, '.'), 1));
  64. if(preg_match('/^(?:jpe?g|png|[gt]if|bmp|swf)$/', $extension)) {
  65. // Disable error reporting
  66. $ER = error_reporting(0);
  67. // Use getimagesize() to find the mime type on images
  68. $mime = getimagesize($filename);
  69. // Turn error reporting back on
  70. error_reporting($ER);
  71. // Return the mime type
  72. if(isset($mime['mime']))
  73. return $mime['mime'];
  74. }
  75. try {
  76. if(function_exists('finfo_open') && ($finfo = finfo_open(FILEINFO_MIME, self::MAGIC_DB)) !== FALSE) {
  77. // Use the fileinfo extension
  78. $mime = finfo_file($finfo, $filename);
  79. finfo_close($finfo);
  80. // Return the mime type
  81. return $mime;
  82. }
  83. } catch(Exception $e) { }
  84. if(ini_get('mime_magic.magicfile') and function_exists('mime_content_type')) {
  85. // Return the mime type using mime_content_type
  86. return mime_content_type($filename);
  87. }
  88. if(!empty($extension) and is_array($mime = Eight::config('mimes.'.$extension))) {
  89. // Return the mime-type guess, based on the extension
  90. return $mime[0];
  91. }
  92. // Unable to find the mime-type
  93. return NO;
  94. }
  95. /**
  96. * Force a download of a file to the user's browser. This function is
  97. * binary-safe and will work with any MIME type that Eight is aware of.
  98. *
  99. * @param string a file path or file name
  100. * @param mixed data to be sent if the filename does not exist
  101. * @param string suggested filename to display in the download
  102. */
  103. public static function download($filename = nil, $data = nil, $nicename = nil) {
  104. if(empty($filename))
  105. return NO;
  106. if(is_file($filename)) {
  107. // Get the real path
  108. $filepath = str_replace('\\', '/', realpath($filename));
  109. // Set filesize
  110. $filesize = filesize($filepath);
  111. // Get filename
  112. $filename = substr(strrchr('/'.$filepath, '/'), 1);
  113. // Get extension
  114. $extension = strtolower(substr(strrchr($filepath, '.'), 1));
  115. } else {
  116. // Get filesize
  117. $filesize = strlen($data);
  118. // Make sure the filename does not have directory info
  119. $filename = substr(strrchr('/'.$filename, '/'), 1);
  120. // Get extension
  121. $extension = strtolower(substr(strrchr($filename, '.'), 1));
  122. }
  123. // Get the mime type of the file
  124. $mime = Eight::config('mimes.'.$extension);
  125. if(empty($mime)) {
  126. // Set a default mime if none was found
  127. $mime = array('application/octet-stream');
  128. }
  129. // Generate the server headers
  130. header('Content-Type: '.$mime[0]);
  131. header('Content-Disposition: attachment; filename="'.(empty($nicename) ? $filename : $nicename).'"');
  132. header('Content-Transfer-Encoding: binary');
  133. header('Content-Length: '.sprintf('%d', $filesize));
  134. // More caching prevention
  135. header('Expires: 0');
  136. if(Eight::user_agent('browser') === 'Internet Explorer') {
  137. // Send IE headers
  138. header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
  139. header('Pragma: public');
  140. } else {
  141. // Send normal headers
  142. header('Pragma: no-cache');
  143. }
  144. // Clear the output buffer
  145. Eight::close_buffers(NO);
  146. if(isset($filepath)) {
  147. // Open the file
  148. $handle = fopen($filepath, 'rb');
  149. // Send the file data
  150. fpassthru($handle);
  151. // Close the file
  152. fclose($handle);
  153. } else {
  154. // Send the file data
  155. echo $data;
  156. }
  157. }
  158. /**
  159. * Split a file into pieces matching a specific size.
  160. *
  161. * @param string file to be split
  162. * @param string directory to output to, defaults to the same directory as the file
  163. * @param integer size, in MB, for each chunk to be
  164. * @return integer The number of pieces that were created.
  165. */
  166. public static function split($filename, $output_dir = NO, $piece_size = 10) {
  167. // Find output dir
  168. $output_dir = ($output_dir == NO) ? pathinfo(str_replace('\\', '/', realpath($filename)), PATHINFO_DIRNAME) : str_replace('\\', '/', realpath($output_dir));
  169. $output_dir = rtrim($output_dir, '/').'/';
  170. // Open files for writing
  171. $input_file = fopen($filename, 'rb');
  172. // Change the piece size to bytes
  173. $piece_size = 1024 * 1024 * (int) $piece_size; // Size in bytes
  174. // Set up reading variables
  175. $read = 0; // Number of bytes read
  176. $piece = 1; // Current piece
  177. $chunk = 1024 * 8; // Chunk size to read
  178. // Split the file
  179. while(!feof($input_file)) {
  180. // Open a new piece
  181. $piece_name = $filename.'.'.str_pad($piece, 3, '0', STR_PAD_LEFT);
  182. $piece_open = @fopen($piece_name, 'wb+') or die('Could not write piece '.$piece_name);
  183. // Fill the current piece
  184. while($read < $piece_size and $data = fread($input_file, $chunk)) {
  185. fwrite($piece_open, $data) or die('Could not write to open piece '.$piece_name);
  186. $read += $chunk;
  187. }
  188. // Close the current piece
  189. fclose($piece_open);
  190. // Prepare to open a new piece
  191. $read = 0;
  192. $piece++;
  193. // Make sure that piece is valid
  194. ($piece < 999) or die('Maximum of 999 pieces exceeded, try a larger piece size');
  195. }
  196. // Close input file
  197. fclose($input_file);
  198. // Returns the number of pieces that were created
  199. return ($piece - 1);
  200. }
  201. /**
  202. * Join a split file into a whole file.
  203. *
  204. * @param string split filename, without .000 extension
  205. * @param string output filename, if different then an the filename
  206. * @return integer The number of pieces that were joined.
  207. */
  208. public static function join($filename, $output = NO) {
  209. if($output == NO)
  210. $output = $filename;
  211. // Set up reading variables
  212. $piece = 1; // Current piece
  213. $chunk = 1024 * 8; // Chunk size to read
  214. // Open output file
  215. $output_file = @fopen($output, 'wb+') or die('Could not open output file '.$output);
  216. // Read each piece
  217. while($piece_open = @fopen(($piece_name = $filename.'.'.str_pad($piece, 3, '0', STR_PAD_LEFT)), 'rb')) {
  218. // Write the piece into the output file
  219. while(!feof($piece_open)) {
  220. fwrite($output_file, fread($piece_open, $chunk));
  221. }
  222. // Close the current piece
  223. fclose($piece_open);
  224. // Prepare for a new piece
  225. $piece++;
  226. // Make sure piece is valid
  227. ($piece < 999) or die('Maximum of 999 pieces exceeded');
  228. }
  229. // Close the output file
  230. fclose($output_file);
  231. // Return the number of pieces joined
  232. return ($piece - 1);
  233. }
  234. /**
  235. * Loops through the mimes config array and finds the extension for a given mime type.
  236. * Might be able to speed this one up a bit...not sure.
  237. *
  238. * @param string mime type
  239. * @return string extension for given mime type
  240. */
  241. public static function mime_to_ext($mime) {
  242. $mimes = Eight::config('mimes');
  243. foreach($mimes as $k=>$m) {
  244. foreach($m as $v) {
  245. if($mime == $v) {
  246. return $k;
  247. }
  248. }
  249. }
  250. }
  251. public static function ext($file) {
  252. if(substr_count($file, ".") > 0) {
  253. return substr(strrchr($file, "."), 1);
  254. } else {
  255. return NULL;
  256. }
  257. }
  258. public static function without_ext($file) {
  259. if(substr_count($file, ".") > 0) {
  260. return substr($file, 0, strrpos($file, "."));
  261. } else {
  262. return $file;
  263. }
  264. }
  265. public static function ext_replace($file, $new) {
  266. $old = self::ext($file);
  267. return substr_replace($file, $new, strlen($old)*-1);
  268. }
  269. /**
  270. * Delete Files
  271. *
  272. * Deletes all files contained in the supplied directory path.
  273. * Files must be writable or owned by the system in order to be deleted.
  274. * If the second parameter is set to true, any directories contained
  275. * within the supplied base directory will be nuked as well.
  276. *
  277. * @access public
  278. * @param string path to file
  279. * @param bool whether to delete any directories found in the path
  280. * @return bool
  281. */
  282. public static function delete_files($path, $del_dir = false, $level = 0) {
  283. // Trim the trailing slash
  284. $path = preg_replace("|^(.+?)/*$|", "\\1", $path);
  285. if ( ! $current_dir = @opendir($path))
  286. return;
  287. while(false !== ($filename = @readdir($current_dir))) {
  288. if ($filename != "." and $filename != "..") {
  289. if (is_dir($path.'/'.$filename)) {
  290. $level++;
  291. self::delete_files($path.'/'.$filename, $del_dir, $level);
  292. } else {
  293. unlink($path.'/'.$filename);
  294. }
  295. }
  296. }
  297. @closedir($current_dir);
  298. if ($del_dir == true and $level > 0) {
  299. @rmdir($path);
  300. }
  301. }
  302. } // End file