PageRenderTime 49ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/fuel/core/classes/file.php

https://bitbucket.org/codeyash/bootstrap
PHP | 805 lines | 515 code | 99 blank | 191 comment | 40 complexity | 07d7238a179f3e5158cf1334e4e63ced MD5 | raw file
Possible License(s): MIT, Apache-2.0
  1. <?php
  2. /**
  3. * Part of the Fuel framework.
  4. *
  5. * @package Fuel
  6. * @version 1.5
  7. * @author Fuel Development Team
  8. * @license MIT License
  9. * @copyright 2010 - 2013 Fuel Development Team
  10. * @link http://fuelphp.com
  11. */
  12. namespace Fuel\Core;
  13. class FileAccessException extends \FuelException {}
  14. class OutsideAreaException extends \OutOfBoundsException {}
  15. class InvalidPathException extends \FileAccessException {}
  16. // ------------------------------------------------------------------------
  17. /**
  18. * File Class
  19. *
  20. * @package Fuel
  21. * @subpackage Core
  22. * @category Core
  23. */
  24. class File
  25. {
  26. /**
  27. * @var array loaded area's
  28. */
  29. protected static $areas = array();
  30. public static function _init()
  31. {
  32. \Config::load('file', true);
  33. // make sure the configured chmod values are octal
  34. $chmod = \Config::get('file.chmod.folders', 0777);
  35. is_string($chmod) and \Config::set('file.chmod.folders', octdec($chmod));
  36. $chmod = \Config::get('file.chmod.files', 0666);
  37. is_string($chmod) and \Config::set('file.chmod.files', octdec($chmod));
  38. static::$areas[null] = \File_Area::forge(\Config::get('file.base_config', array()));
  39. foreach (\Config::get('file.areas', array()) as $name => $config)
  40. {
  41. static::$areas[$name] = \File_Area::forge($config);
  42. }
  43. }
  44. public static function forge(array $config = array())
  45. {
  46. return \File_Area::forge($config);
  47. }
  48. /**
  49. * Instance
  50. *
  51. * @param string|File_Area|null file area name, object or null for base area
  52. * @return File_Area
  53. */
  54. public static function instance($area = null)
  55. {
  56. if ($area instanceof File_Area)
  57. {
  58. return $area;
  59. }
  60. $instance = array_key_exists($area, static::$areas) ? static::$areas[$area] : false;
  61. if ($instance === false)
  62. {
  63. throw new \InvalidArgumentException('There is no file instance named "'.$area.'".');
  64. }
  65. return $instance;
  66. }
  67. /**
  68. * File & directory objects factory
  69. *
  70. * @param string path to the file or directory
  71. * @param array configuration items
  72. * @param string|File_Area|null file area name, object or null for base area
  73. * @return File_Handler_File
  74. */
  75. public static function get($path, array $config = array(), $area = null)
  76. {
  77. return static::instance($area)->get_handler($path, $config);
  78. }
  79. /**
  80. * Get the url.
  81. *
  82. * @return bool
  83. */
  84. public static function get_url($path, array $config = array(), $area = null)
  85. {
  86. return static::get($path, $config, $area)->get_url();
  87. }
  88. /**
  89. * Create an empty file
  90. *
  91. * @param string directory where to create file
  92. * @param string filename
  93. * @param string|File_Area|null file area name, object or null for base area
  94. * @return bool
  95. */
  96. public static function create($basepath, $name, $contents = null, $area = null)
  97. {
  98. $basepath = rtrim(static::instance($area)->get_path($basepath), '\\/').DS;
  99. $new_file = static::instance($area)->get_path($basepath.$name);
  100. if ( ! is_dir($basepath) or ! is_writable($basepath))
  101. {
  102. throw new \InvalidPathException('Invalid basepath: "'.$basepath.'", cannot create file at this location.');
  103. }
  104. elseif (file_exists($new_file))
  105. {
  106. throw new \FileAccessException('File: "'.$new_file.'" already exists, cannot be created.');
  107. }
  108. $file = static::open_file(@fopen($new_file, 'c'), true, $area);
  109. fwrite($file, $contents);
  110. static::close_file($file, $area);
  111. return true;
  112. }
  113. /**
  114. * Create an empty directory
  115. *
  116. * @param string directory where to create new dir
  117. * @param string dirname
  118. * @param int (octal) file permissions
  119. * @param string|File_Area|null file area name, object or null for non-specific
  120. * @return bool
  121. */
  122. public static function create_dir($basepath, $name, $chmod = null, $area = null)
  123. {
  124. $basepath = rtrim(static::instance($area)->get_path($basepath), '\\/').DS;
  125. $new_dir = static::instance($area)->get_path($basepath.$name);
  126. is_null($chmod) and $chmod = \Config::get('file.chmod.folders', 0777);
  127. if ( ! is_dir($basepath) or ! is_writable($basepath))
  128. {
  129. throw new \InvalidPathException('Invalid basepath: "'.$basepath.'", cannot create directory at this location.');
  130. }
  131. elseif (file_exists($new_dir))
  132. {
  133. throw new \FileAccessException('Directory: "'.$new_dir.'" exists already, cannot be created.');
  134. }
  135. $recursive = (strpos($name, '/') !== false or strpos($name, '\\') !== false);
  136. return mkdir($new_dir, $chmod, $recursive);
  137. }
  138. /**
  139. * Read file
  140. *
  141. * @param string file to read
  142. * @param bool whether to use readfile() or file_get_contents()
  143. * @param string|File_Area|null file area name, object or null for base area
  144. * @return IO|string file contents
  145. */
  146. public static function read($path, $as_string = false, $area = null)
  147. {
  148. $path = static::instance($area)->get_path($path);
  149. if( ! file_exists($path) or ! is_file($path))
  150. {
  151. throw new \InvalidPathException('Cannot read file: "'.$path.'", file does not exists.');
  152. }
  153. $file = static::open_file(@fopen($path, 'r'), LOCK_SH, $area);
  154. $return = $as_string ? file_get_contents($path) : readfile($path);
  155. static::close_file($file, $area);
  156. return $return;
  157. }
  158. /**
  159. * Read directory
  160. *
  161. * @param string directory to read
  162. * @param int depth to recurse directory, 1 is only current and 0 or smaller is unlimited
  163. * @param Array|null array of partial regexes or non-array for default
  164. * @param string|File_Area|null file area name, object or null for base area
  165. * @return array directory contents in an array
  166. */
  167. public static function read_dir($path, $depth = 0, $filter = null, $area = null)
  168. {
  169. $path = rtrim(static::instance($area)->get_path($path), '\\/').DS;
  170. if ( ! is_dir($path))
  171. {
  172. throw new \InvalidPathException('Invalid path: "'.$path.'", directory cannot be read.');
  173. }
  174. if ( ! $fp = @opendir($path))
  175. {
  176. throw new \FileAccessException('Could not open directory: "'.$path.'" for reading.');
  177. }
  178. // Use default when not set
  179. if ( ! is_array($filter))
  180. {
  181. $filter = array('!^\.');
  182. if ($extensions = static::instance($area)->extensions())
  183. {
  184. foreach($extensions as $ext)
  185. {
  186. $filter[] = '\.'.$ext.'$';
  187. }
  188. }
  189. }
  190. $files = array();
  191. $dirs = array();
  192. $new_depth = $depth - 1;
  193. while (false !== ($file = readdir($fp)))
  194. {
  195. // Remove '.', '..'
  196. if (in_array($file, array('.', '..')))
  197. {
  198. continue;
  199. }
  200. // use filters when given
  201. elseif ( ! empty($filter))
  202. {
  203. $continue = false; // whether or not to continue
  204. $matched = false; // whether any positive pattern matched
  205. $positive = false; // whether positive filters are present
  206. foreach($filter as $f => $type)
  207. {
  208. if (is_numeric($f))
  209. {
  210. // generic rule
  211. $f = $type;
  212. }
  213. else
  214. {
  215. // type specific rule
  216. $is_file = is_file($path.$file);
  217. if (($type === 'file' and ! $is_file) or ($type !== 'file' and $is_file))
  218. {
  219. continue;
  220. }
  221. }
  222. $not = substr($f, 0, 1) == '!'; // whether it's a negative condition
  223. $f = $not ? substr($f, 1) : $f;
  224. // on negative condition a match leads to a continue
  225. if (($match = preg_match('/'.$f.'/uiD', $file) > 0) and $not)
  226. {
  227. $continue = true;
  228. }
  229. $positive = $positive ?: ! $not; // whether a positive condition was encountered
  230. $matched = $matched ?: ($match and ! $not); // whether one of the filters has matched
  231. }
  232. // continue when negative matched or when positive filters and nothing matched
  233. if ($continue or $positive and ! $matched)
  234. {
  235. continue;
  236. }
  237. }
  238. if (@is_dir($path.$file))
  239. {
  240. // Use recursion when depth not depleted or not limited...
  241. if ($depth < 1 or $new_depth > 0)
  242. {
  243. $dirs[$file.DS] = static::read_dir($path.$file.DS, $new_depth, $filter, $area);
  244. }
  245. // ... or set dir to false when not read
  246. else
  247. {
  248. $dirs[$file.DS] = false;
  249. }
  250. }
  251. else
  252. {
  253. $files[] = $file;
  254. }
  255. }
  256. closedir($fp);
  257. // sort dirs & files naturally and return array with dirs on top and files
  258. uksort($dirs, 'strnatcasecmp');
  259. natcasesort($files);
  260. return array_merge($dirs, $files);
  261. }
  262. /**
  263. * Update a file
  264. *
  265. * @param string directory where to write the file
  266. * @param string filename
  267. * @param string|File_Area|null file area name, object or null for base area
  268. * @return bool
  269. */
  270. public static function update($basepath, $name, $contents = null, $area = null)
  271. {
  272. $basepath = rtrim(static::instance($area)->get_path($basepath), '\\/').DS;
  273. $new_file = static::instance($area)->get_path($basepath.$name);
  274. if ( ! $file = static::open_file(@fopen($new_file, 'w'), true, $area))
  275. {
  276. if ( ! is_dir($basepath) or ! is_writable($basepath))
  277. {
  278. throw new \InvalidPathException('Invalid basepath: "'.$basepath.'", cannot update a file at this location.');
  279. }
  280. throw new \FileAccessException('No write access to: "'.$basepath.'", cannot update a file.');
  281. }
  282. fwrite($file, $contents);
  283. static::close_file($file, $area);
  284. return true;
  285. }
  286. /**
  287. * Append to a file
  288. *
  289. * @param string directory where to write the file
  290. * @param string filename
  291. * @param string|File_Area|null file area name, object or null for base area
  292. * @return bool
  293. */
  294. public static function append($basepath, $name, $contents = null, $area = null)
  295. {
  296. $basepath = rtrim(static::instance($area)->get_path($basepath), '\\/').DS;
  297. $new_file = static::instance($area)->get_path($basepath.$name);
  298. if ( ! file_exists($new_file))
  299. {
  300. throw new \FileAccessException('File: "'.$new_file.'" does not exist, cannot be appended.');
  301. }
  302. if ( ! $file = static::open_file(@fopen($new_file, 'a'), true, $area))
  303. {
  304. if ( ! is_dir($basepath) or ! is_writable($basepath))
  305. {
  306. throw new \InvalidPathException('Invalid basepath: "'.$basepath.'", cannot append to a file at this location.');
  307. }
  308. throw new \FileAccessException('No write access, cannot append to the file: "'.$file.'".');
  309. }
  310. fwrite($file, $contents);
  311. static::close_file($file, $area);
  312. return true;
  313. }
  314. /**
  315. * Get the octal permissions for a file or directory
  316. *
  317. * @param string path to the file or directory
  318. * @param mixed file area name, object or null for base area
  319. * $return string octal file permissions
  320. */
  321. public static function get_permissions($path, $area = null)
  322. {
  323. $path = static::instance($area)->get_path($path);
  324. if ( ! file_exists($path))
  325. {
  326. throw new \InvalidPathException('Path: "'.$path.'" is not a directory or a file, cannot get permissions.');
  327. }
  328. return substr(sprintf('%o', fileperms($path)), -4);
  329. }
  330. /**
  331. * Get a file's or directory's created or modified timestamp.
  332. *
  333. * @param string path to the file or directory
  334. * @param string modified or created
  335. * @param mixed file area name, object or null for base area
  336. * @return int Unix Timestamp
  337. */
  338. public static function get_time($path, $type = 'modified', $area = null)
  339. {
  340. $path = static::instance($area)->get_path($path);
  341. if ( ! file_exists($path))
  342. {
  343. throw new \InvalidPathException('Path: "'.$path.'" is not a directory or a file, cannot get creation timestamp.');
  344. }
  345. if ($type === 'modified')
  346. {
  347. return filemtime($path);
  348. }
  349. elseif ($type === 'created')
  350. {
  351. return filectime($path);
  352. }
  353. else
  354. {
  355. throw new \UnexpectedValueException('File::time $type must be "modified" or "created".');
  356. }
  357. }
  358. /**
  359. * Get a file's size.
  360. *
  361. * @param string path to the file or directory
  362. * @param mixed file area name, object or null for base area
  363. * @return int the file's size in bytes
  364. */
  365. public static function get_size($path, $area = null)
  366. {
  367. $path = static::instance($area)->get_path($path);
  368. if ( ! file_exists($path))
  369. {
  370. throw new \InvalidPathException('Path: "'.$path.'" is not a directory or a file, cannot get size.');
  371. }
  372. return filesize($path);
  373. }
  374. /**
  375. * Rename directory or file
  376. *
  377. * @param string path to file or directory to rename
  378. * @param string new path (full path, can also cause move)
  379. * @param string|File_Area|null source path file area name, object or null for non-specific
  380. * @param string|File_Area|null target path file area name, object or null for non-specific. Defaults to source_area if not set.
  381. * @return bool
  382. */
  383. public static function rename($path, $new_path, $source_area = null, $target_area = null)
  384. {
  385. $path = static::instance($source_area)->get_path($path);
  386. $new_path = static::instance($target_area ?: $source_area)->get_path($new_path);
  387. return rename($path, $new_path);
  388. }
  389. /**
  390. * Alias for rename(), not needed but consistent with other methods
  391. */
  392. public static function rename_dir($path, $new_path, $source_area = null, $target_area = null)
  393. {
  394. return static::rename($path, $new_path, $source_area, $target_area);
  395. }
  396. /**
  397. * Copy file
  398. *
  399. * @param string path to file to copy
  400. * @param string new base directory (full path)
  401. * @param string|File_Area|null source path file area name, object or null for non-specific
  402. * @param string|File_Area|null target path file area name, object or null for non-specific. Defaults to source_area if not set.
  403. * @return bool
  404. */
  405. public static function copy($path, $new_path, $source_area = null, $target_area = null)
  406. {
  407. $path = static::instance($source_area)->get_path($path);
  408. $new_path = static::instance($target_area ?: $source_area)->get_path($new_path);
  409. if ( ! is_file($path))
  410. {
  411. throw new \InvalidPathException('Cannot copy file: given path: "'.$path.'" is not a file.');
  412. }
  413. elseif (file_exists($new_path))
  414. {
  415. throw new \FileAccessException('Cannot copy file: new path: "'.$new_path.'" already exists.');
  416. }
  417. return copy($path, $new_path);
  418. }
  419. /**
  420. * Copy directory
  421. *
  422. * @param string path to directory which contents will be copied
  423. * @param string new base directory (full path)
  424. * @param string|File_Area|null source path file area name, object or null for non-specific
  425. * @param string|File_Area|null target path file area name, object or null for non-specific. Defaults to source_area if not set.
  426. * @return bool
  427. * @throws FileAccessException when something went wrong
  428. */
  429. public static function copy_dir($path, $new_path, $source_area = null, $target_area = null)
  430. {
  431. $target_area = $target_area ?: $source_area;
  432. $path = rtrim(static::instance($source_area)->get_path($path), '\\/').DS;
  433. $new_path = rtrim(static::instance($target_area)->get_path($new_path), '\\/').DS;
  434. if ( ! is_dir($path))
  435. {
  436. throw new \InvalidPathException('Cannot copy directory: given path: "'.$path.'" is not a directory: '.$path);
  437. }
  438. elseif ( ! file_exists($new_path))
  439. {
  440. $newpath_dirname = pathinfo($new_path, PATHINFO_DIRNAME);
  441. static::create_dir($newpath_dirname, pathinfo($new_path, PATHINFO_BASENAME), fileperms($newpath_dirname) ?: 0777, $target_area);
  442. }
  443. $files = static::read_dir($path, -1, array(), $source_area);
  444. foreach ($files as $dir => $file)
  445. {
  446. if (is_array($file))
  447. {
  448. $check = static::create_dir($new_path.DS, substr($dir, 0, -1), fileperms($path.$dir) ?: 0777, $target_area);
  449. $check and static::copy_dir($path.$dir.DS, $new_path.$dir, $source_area, $target_area);
  450. }
  451. else
  452. {
  453. $check = static::copy($path.$file, $new_path.$file, $source_area, $target_area);
  454. }
  455. // abort if something went wrong
  456. if ( ! $check)
  457. {
  458. throw new \FileAccessException('Directory copy aborted prematurely, part of the operation failed during copying: '.(is_array($file) ? $dir : $file));
  459. }
  460. }
  461. }
  462. /**
  463. * Create a new symlink
  464. *
  465. * @param string target of symlink
  466. * @param string destination of symlink
  467. * @param bool true for file, false for directory
  468. * @param string|File_Area|null file area name, object or null for base area
  469. * @return bool
  470. */
  471. public static function symlink($path, $link_path, $is_file = true, $area = null)
  472. {
  473. $path = rtrim(static::instance($area)->get_path($path), '\\/');
  474. $link_path = rtrim(static::instance($area)->get_path($link_path), '\\/');
  475. if ($is_file and ! is_file($path))
  476. {
  477. throw new \InvalidPathException('Cannot symlink: given file: "'.$path.'" does not exist.');
  478. }
  479. elseif ( ! $is_file and ! is_dir($path))
  480. {
  481. throw new \InvalidPathException('Cannot symlink: given directory: "'.$path.'" does not exist.');
  482. }
  483. elseif (file_exists($link_path))
  484. {
  485. throw new \FileAccessException('Cannot symlink: link: "'.$link_path.'" already exists.');
  486. }
  487. return symlink($path, $link_path);
  488. }
  489. /**
  490. * Delete file
  491. *
  492. * @param string path to file to delete
  493. * @param string|File_Area|null file area name, object or null for base area
  494. * @return bool
  495. */
  496. public static function delete($path, $area = null)
  497. {
  498. $path = rtrim(static::instance($area)->get_path($path), '\\/');
  499. if ( ! is_file($path) and ! is_link($path))
  500. {
  501. throw new \InvalidPathException('Cannot delete file: given path "'.$path.'" is not a file.');
  502. }
  503. return unlink($path);
  504. }
  505. /**
  506. * Delete directory
  507. *
  508. * @param string path to directory to delete
  509. * @param bool whether to also delete contents of subdirectories
  510. * @param bool whether to delete the parent dir itself when empty
  511. * @param string|File_Area|null file area name, object or null for base area
  512. * @return bool
  513. */
  514. public static function delete_dir($path, $recursive = true, $delete_top = true, $area = null)
  515. {
  516. $path = rtrim(static::instance($area)->get_path($path), '\\/').DS;
  517. if ( ! is_dir($path))
  518. {
  519. throw new \InvalidPathException('Cannot delete directory: given path: "'.$path.'" is not a directory.');
  520. }
  521. $files = static::read_dir($path, -1, array(), $area);
  522. $not_empty = false;
  523. $check = true;
  524. foreach ($files as $dir => $file)
  525. {
  526. if (is_array($file))
  527. {
  528. if ($recursive)
  529. {
  530. $check = static::delete_dir($path.$dir, true, true, $area);
  531. }
  532. else
  533. {
  534. $not_empty = true;
  535. }
  536. }
  537. else
  538. {
  539. $check = static::delete($path.$file, $area);
  540. }
  541. // abort if something went wrong
  542. if ( ! $check)
  543. {
  544. throw new \FileAccessException('Directory deletion aborted prematurely, part of the operation failed.');
  545. }
  546. }
  547. if ( ! $not_empty and $delete_top)
  548. {
  549. return rmdir($path);
  550. }
  551. return true;
  552. }
  553. /**
  554. * Open and lock file
  555. *
  556. * @param resource|string file resource or path
  557. * @param constant either valid lock constant or true=LOCK_EX / false=LOCK_UN
  558. * @param string|File_Area|null file area name, object or null for base area
  559. */
  560. public static function open_file($path, $lock = true, $area = null)
  561. {
  562. if (is_string($path))
  563. {
  564. $path = static::instance($area)->get_path($path);
  565. $resource = fopen($path, 'r+');
  566. }
  567. else
  568. {
  569. $resource = $path;
  570. }
  571. // Make sure the parameter is a valid resource
  572. if ( ! is_resource($resource))
  573. {
  574. return false;
  575. }
  576. // If locks aren't used, don't lock
  577. if ( ! static::instance($area)->use_locks())
  578. {
  579. return $resource;
  580. }
  581. // Accept valid lock constant or set to LOCK_EX
  582. $lock = in_array($lock, array(LOCK_SH, LOCK_EX, LOCK_NB)) ? $lock : LOCK_EX;
  583. // Try to get a lock, timeout after 5 seconds
  584. $lock_mtime = microtime(true);
  585. while ( ! flock($resource, $lock))
  586. {
  587. if (microtime(true) - $lock_mtime > 5)
  588. {
  589. throw new \FileAccessException('Could not secure file lock, timed out after 5 seconds.');
  590. }
  591. }
  592. return $resource;
  593. }
  594. /**
  595. * Close file resource & unlock
  596. *
  597. * @param resource open file resource
  598. * @param string|File_Area|null file area name, object or null for base area
  599. */
  600. public static function close_file($resource, $area = null)
  601. {
  602. fclose($resource);
  603. // If locks aren't used, don't unlock
  604. if ( ! static::instance($area)->use_locks())
  605. {
  606. return;
  607. }
  608. flock($resource, LOCK_UN);
  609. }
  610. /**
  611. * Get detailed information about a file
  612. *
  613. * @param string file path
  614. * @param string|File_Area|null file area name, object or null for base area
  615. */
  616. public static function file_info($path, $area = null)
  617. {
  618. $info = array(
  619. 'original' => $path,
  620. 'realpath' => '',
  621. 'dirname' => '',
  622. 'basename' => '',
  623. 'filename' => '',
  624. 'extension' => '',
  625. 'mimetype' => '',
  626. 'charset' => '',
  627. 'size' => 0,
  628. 'permissions' => '',
  629. 'time_created' => '',
  630. 'time_modified' => '',
  631. );
  632. if ( ! $info['realpath'] = static::instance($area)->get_path($path) or ! file_exists($info['realpath']))
  633. {
  634. throw new \InvalidPathException('Filename given is not a valid file.');
  635. }
  636. $info = array_merge($info, pathinfo($info['realpath']));
  637. if ( ! $fileinfo = new \finfo(FILEINFO_MIME, \Config::get('file.magic_file', null)))
  638. {
  639. throw new \InvalidArgumentException('Can not retrieve information about this file.');
  640. }
  641. $fileinfo = explode(';', $fileinfo->file($info['realpath']));
  642. $info['mimetype'] = isset($fileinfo[0]) ? $fileinfo[0] : 'application/octet-stream';
  643. if (isset($fileinfo[1]))
  644. {
  645. $fileinfo = explode('=', $fileinfo[1]);
  646. $info['charset'] = isset($fileinfo[1]) ? $fileinfo[1] : '';
  647. }
  648. $info['size'] = static::get_size($info['realpath'], $area);
  649. $info['permissions'] = static::get_permissions($info['realpath'], $area);
  650. $info['time_created'] = static::get_time($info['realpath'], $type = 'created', $area);
  651. $info['time_modified'] = static::get_time($info['realpath'], $type = 'modified', $area);
  652. return $info;
  653. }
  654. /**
  655. * Download a file
  656. *
  657. * @param string file path
  658. * @param string|null custom name for the file to be downloaded
  659. * @param string|null custom mime type or null for file mime type
  660. * @param string|File_Area|null file area name, object or null for base area
  661. */
  662. public static function download($path, $name = null, $mime = null, $area = null)
  663. {
  664. $info = static::file_info($path, $area);
  665. $class = get_called_class();
  666. empty($mime) or $info['mimetype'] = $mime;
  667. empty($name) or $info['basename'] = $name;
  668. \Event::register('shutdown', function () use($info, $area, $class) {
  669. if ( ! $file = call_user_func(array($class, 'open_file'), @fopen($info['realpath'], 'rb'), LOCK_SH, $area))
  670. {
  671. throw new \FileAccessException('Filename given could not be opened for download.');
  672. }
  673. while (ob_get_level() > 0)
  674. {
  675. ob_end_clean();
  676. }
  677. ini_get('zlib.output_compression') and ini_set('zlib.output_compression', 0);
  678. ! ini_get('safe_mode') and set_time_limit(0);
  679. header('Content-Type: '.$info['mimetype']);
  680. header('Content-Disposition: attachment; filename="'.$info['basename'].'"');
  681. header('Content-Description: File Transfer');
  682. header('Content-Length: '.$info['size']);
  683. header('Content-Transfer-Encoding: binary');
  684. header('Expires: 0');
  685. header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
  686. while( ! feof($file))
  687. {
  688. echo fread($file, 2048);
  689. }
  690. call_user_func(array($class, 'close_file'), $file, $area);
  691. });
  692. exit;
  693. }
  694. }