area.php 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  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 File_Area
  14. {
  15. /**
  16. * @var string path to basedir restriction, null for no restriction
  17. */
  18. protected $basedir = null;
  19. /**
  20. * @var array array of allowed extensions, null for all
  21. */
  22. protected $extensions = null;
  23. /**
  24. * @var string base url for files, null for not available
  25. */
  26. protected $url = null;
  27. /**
  28. * @var bool whether or not to use file locks when doing file operations
  29. */
  30. protected $use_locks = false;
  31. /**
  32. * @var array contains file handler per file extension
  33. */
  34. protected $file_handlers = array();
  35. protected function __construct(array $config = array())
  36. {
  37. foreach ($config as $key => $value)
  38. {
  39. if (property_exists($this, $key))
  40. {
  41. $this->{$key} = $value;
  42. }
  43. }
  44. if ( ! empty($this->basedir))
  45. {
  46. $this->basedir = realpath($this->basedir) ?: $this->basedir;
  47. }
  48. }
  49. /**
  50. * Factory for area objects
  51. *
  52. * @param array
  53. * @return File_Area
  54. */
  55. public static function forge(array $config = array())
  56. {
  57. return new static($config);
  58. }
  59. /**
  60. * Handler factory for given path
  61. *
  62. * @param string path to file or directory
  63. * @param array optional config
  64. * @return File_Handler_File
  65. * @throws FileAccessException when outside basedir restriction or disallowed file extension
  66. */
  67. public function get_handler($path, array $config = array(), $content = array())
  68. {
  69. $path = $this->get_path($path);
  70. if (is_file($path))
  71. {
  72. $info = pathinfo($path);
  73. // deal with path names without an extension
  74. isset($info['extension']) or $info['extension'] = '';
  75. // check file extension
  76. if ( ! empty($this->extensions) && ! in_array($info['extension'], $this->extensions))
  77. {
  78. throw new \FileAccessException('File operation not allowed: disallowed file extension.');
  79. }
  80. // create specific handler when available
  81. if (array_key_exists($info['extension'], $this->file_handlers))
  82. {
  83. $class = '\\'.ltrim($this->file_handlers[$info['extension']], '\\');
  84. return $class::forge($path, $config, $this);
  85. }
  86. return \File_Handler_File::forge($path, $config, $this);
  87. }
  88. elseif (is_dir($path))
  89. {
  90. return \File_Handler_Directory::forge($path, $config, $this, $content);
  91. }
  92. // still here? path is invalid
  93. throw new \FileAccessException('Invalid path for file or directory.');
  94. }
  95. /**
  96. * Does this area use file locks?
  97. *
  98. * @return bool
  99. */
  100. public function use_locks()
  101. {
  102. return $this->use_locks;
  103. }
  104. /**
  105. * Are the shown extensions limited, and if so to which?
  106. *
  107. * @return array
  108. */
  109. public function extensions()
  110. {
  111. return $this->extensions;
  112. }
  113. /**
  114. * Translate relative path to real path, throws error when operation is not allowed
  115. *
  116. * @param string
  117. * @return string
  118. * @throws FileAccessException when outside basedir restriction or disallowed file extension
  119. */
  120. public function get_path($path)
  121. {
  122. $pathinfo = is_dir($path) ? array('dirname' => $path, 'extension' => null, 'basename' => '') : pathinfo($path);
  123. // do we have a basedir, and is the path already prefixed by the basedir? then just deal with the double dots...
  124. if ( ! empty($this->basedir) && substr($pathinfo['dirname'], 0, strlen($this->basedir)) == $this->basedir)
  125. {
  126. $pathinfo['dirname'] = realpath($pathinfo['dirname']);
  127. }
  128. else
  129. {
  130. // attempt to get the realpath(), otherwise just use path with any double dots taken out when basedir is set (for security)
  131. $pathinfo['dirname'] = ( ! empty($this->basedir) ? realpath($this->basedir.DS.$pathinfo['dirname']) : realpath($pathinfo['dirname']) )
  132. ?: ( ! empty($this->basedir) ? $this->basedir.DS.str_replace('..', '', $pathinfo['dirname']) : $pathinfo['dirname']);
  133. }
  134. // basedir prefix is required when it is set (may cause unexpected errors when realpath doesn't work)
  135. if ( ! empty($this->basedir) && substr($pathinfo['dirname'], 0, strlen($this->basedir)) != $this->basedir)
  136. {
  137. throw new \OutsideAreaException('File operation not allowed: given path is outside the basedir for this area.');
  138. }
  139. // check file extension
  140. if ( ! empty(static::$extensions) && array_key_exists($pathinfo['extension'], static::$extensions))
  141. {
  142. throw new \FileAccessException('File operation not allowed: disallowed file extension.');
  143. }
  144. return $pathinfo['dirname'].DS.$pathinfo['basename'];
  145. }
  146. /**
  147. * Translate relative path to accessible path, throws error when operation is not allowed
  148. *
  149. * @param string
  150. * @return string
  151. * @throws LogicException when no url is set or no basedir is set and file is outside DOCROOT
  152. */
  153. public function get_url($path)
  154. {
  155. if(empty($this->url))
  156. {
  157. throw new \LogicException('File operation now allowed: cannot create a file url without an area url.');
  158. }
  159. $path = $this->get_path($path);
  160. $basedir = $this->basedir;
  161. empty($basedir) and $basedir = DOCROOT;
  162. if(stripos($path, $basedir) !== 0)
  163. {
  164. throw new \LogicException('File operation not allowed: cannot create file url whithout a basedir and file outside DOCROOT.');
  165. }
  166. return rtrim($this->url, '/').'/'.ltrim(str_replace(DS, '/', substr($path, strlen($basedir))),'/');
  167. }
  168. /* -------------------------------------------------------------------------------------
  169. * Allow all File methods to be used from an area directly
  170. * ------------------------------------------------------------------------------------- */
  171. public function create($basepath, $name, $contents = null)
  172. {
  173. return \File::create($basepath, $name, $contents, $this);
  174. }
  175. public function create_dir($basepath, $name, $chmod = null)
  176. {
  177. return \File::create_dir($basepath, $name, $chmod, $this);
  178. }
  179. public function read($path, $as_string = false)
  180. {
  181. return \File::read($path, $as_string, $this);
  182. }
  183. public function read_dir($path, $depth = 0, $filter = null)
  184. {
  185. $content = \File::read_dir($path, $depth, $filter, $this);
  186. return $this->get_handler($path, array(), $content);
  187. }
  188. public function rename($path, $new_path)
  189. {
  190. return \File::rename($path, $new_path, $this);
  191. }
  192. public function rename_dir($path, $new_path)
  193. {
  194. return \File::rename_dir($path, $new_path, $this);
  195. }
  196. public function copy($path, $new_path)
  197. {
  198. return \File::copy($path, $new_path, $this);
  199. }
  200. public function copy_dir($path, $new_path)
  201. {
  202. return \File::copy_dir($path, $new_path, $this);
  203. }
  204. public function delete($path)
  205. {
  206. return \File::delete($path, $this);
  207. }
  208. public function delete_dir($path, $recursive = true, $delete_top = true)
  209. {
  210. return \File::delete_dir($path, $recursive, $delete_top, $this);
  211. }
  212. public function update($basepath, $name, $new_content)
  213. {
  214. return \File::update($basepath, $name, $new_content, $this);
  215. }
  216. public function get_permissions($path)
  217. {
  218. return \File::get_permissions($path, $this);
  219. }
  220. public function get_time($path, $type)
  221. {
  222. return \File::get_time($path, $type, $this);
  223. }
  224. public function get_size($path)
  225. {
  226. return \File::get_size($path, $this);
  227. }
  228. }