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

/core/model/modx/modfilehandler.class.php

https://github.com/krisj/revolution
PHP | 502 lines | 224 code | 53 blank | 225 comment | 29 complexity | 7939459f1f4b28a576a6a812bf4021a3 MD5 | raw file
Possible License(s): AGPL-1.0, LGPL-2.1, GPL-2.0, GPL-3.0, LGPL-2.0
  1. <?php
  2. /**
  3. * Assists with directory/file manipulation
  4. *
  5. * @package modx
  6. */
  7. class modFileHandler {
  8. public $config = array();
  9. public $context = null;
  10. /**
  11. * The constructor for the modFileHandler class
  12. *
  13. * @param modX &$modx A reference to the modX object.
  14. * @param array $config An array of options.
  15. */
  16. function __construct(modX &$modx, array $config = array()) {
  17. $this->modx =& $modx;
  18. $this->config = array_merge($this->config, $this->modx->_userConfig, $config);
  19. if (!isset($this->config['context'])) {
  20. $this->config['context'] = $this->modx->context->get('key');
  21. }
  22. $this->context = $this->modx->getContext($this->config['context']);
  23. }
  24. /**
  25. * Dynamically creates a modDirectory or modFile object.
  26. *
  27. * The object is created based on the type of resource provided.
  28. *
  29. * @param string $path The absolute path to the filesystem resource.
  30. * @param array $options Optional. An array of options for the object.
  31. * @param string $overrideClass Optional. If provided, will force creation
  32. * of the object as the specified class.
  33. * @return mixed The appropriate modFile/modDirectory object
  34. */
  35. public function make($path, array $options = array(), $overrideClass = '') {
  36. $path = $this->sanitizePath($path);
  37. $class = 'modFile';
  38. if (!empty($overrideClass)) {
  39. $class = $overrideClass;
  40. } else {
  41. if (is_dir($path)) {
  42. $path = $this->postfixSlash($path);
  43. $class = 'modDirectory';
  44. } else {
  45. $class = 'modFile';
  46. }
  47. }
  48. return new $class($this, $path, $options);
  49. }
  50. /**
  51. * Get the modX base path for the user.
  52. *
  53. * @param string $prependBasePath If true, will prepend the modX base path
  54. * to the return value. Defaults to true.
  55. * @return string The base path
  56. */
  57. public function getBasePath() {
  58. $root = $this->context->getOption('filemanager_path', '', $this->config);
  59. /* expand placeholders */
  60. $root = str_replace(array(
  61. '{base_path}',
  62. '{core_path}',
  63. '{assets_path}',
  64. ), array(
  65. $this->context->getOption('base_path', MODX_BASE_PATH, $this->config),
  66. $this->context->getOption('core_path', MODX_CORE_PATH, $this->config),
  67. $this->context->getOption('assets_path', MODX_ASSETS_PATH, $this->config),
  68. ), $root);
  69. return !empty($root) ? $this->postfixSlash($root) : $root;
  70. }
  71. /**
  72. * Get base URL of file manager
  73. */
  74. public function getBaseUrl() {
  75. $baseUrl = $this->context->getOption('filemanager_url', $this->context->getOption('rb_base_url', MODX_BASE_URL, $this->config), $this->config);
  76. /* expand placeholders */
  77. $baseUrl = str_replace(array(
  78. '{base_url}',
  79. '{core_url}',
  80. '{assets_url}',
  81. ), array(
  82. $this->context->getOption('base_url', MODX_BASE_PATH, $this->config),
  83. $this->context->getOption('core_url', MODX_CORE_PATH, $this->config),
  84. $this->context->getOption('assets_url', MODX_ASSETS_PATH, $this->config),
  85. ), $baseUrl);
  86. return !empty($baseUrl) ? $this->postfixSlash($baseUrl) : $baseUrl;
  87. }
  88. /**
  89. * Sanitize the specified path
  90. *
  91. * @param string $path The path to clean
  92. * @return string The sanitized path
  93. */
  94. public function sanitizePath($path) {
  95. $path = str_replace(array('../', './'), '', $path);
  96. $path = strtr($path, '\\', '/');
  97. $path = str_replace('//', '/', $path);
  98. return $path;
  99. }
  100. public function postfixSlash($path) {
  101. $len = strlen($path);
  102. if (substr($path, $len - 1, $len) != '/') {
  103. $path .= '/';
  104. }
  105. return $path;
  106. }
  107. public function getDirectoryFromFile($fileName) {
  108. $dir = dirname($fileName);
  109. return $this->postfixSlash($dir);
  110. }
  111. /**
  112. * Tells if a file is a binary file or not.
  113. *
  114. * @param string $file
  115. * @return boolean True if a binary file.
  116. */
  117. public function isBinary($file) {
  118. if (file_exists($file)) {
  119. if (!is_file($file)) return false;
  120. $fh = @fopen($file, 'r');
  121. $blk = @fread($fh, 512);
  122. @fclose($fh);
  123. @clearstatcache();
  124. return (substr_count($blk, "^ -~" /*. "^\r\n"*/) / 512 > 0.3) || (substr_count($blk, "\x00") > 0) ? false : true;
  125. }
  126. return false;
  127. }
  128. }
  129. /**
  130. * Abstract class for handling file system resources (files or folders). Not to
  131. * be instantiated directly - you should implement your own derivative class.
  132. */
  133. abstract class modFileSystemResource {
  134. /**
  135. * @var string The absolute path of the file system resource
  136. */
  137. protected $path;
  138. /**
  139. * @var modFileHandler A reference to a modFileHandler instance
  140. */
  141. public $fileHandler;
  142. /**
  143. * @var array An array of file system resource specific options
  144. */
  145. public $options = array();
  146. /**
  147. * Constructor for modFileSystemResource
  148. *
  149. * @param modFileHandler $fh A reference to the modFileHandler object
  150. * @param string $path The path to the fs resource
  151. * @param array $options An array of specific options
  152. */
  153. function __construct(modFileHandler &$fh, $path, array $options = array()) {
  154. $this->fileHandler =& $fh;
  155. $this->path = $path;
  156. $this->options = array_merge(array(
  157. ), $options);
  158. }
  159. /**
  160. * Get the path of the fs resource.
  161. * @return string The path of the fs resource
  162. */
  163. public function getPath() {
  164. return $this->path;
  165. }
  166. /**
  167. * Chmods the resource to the specified mode.
  168. *
  169. * @param octal $mode
  170. * @return boolean True if successful
  171. */
  172. public function chmod($mode) {
  173. $mode = $this->parseMode($mode);
  174. return @chmod($this->path, $mode);
  175. }
  176. /**
  177. * Sets the group permission for the fs resource
  178. * @param mixed $grp
  179. * @return boolean True if successful
  180. */
  181. public function chgrp($grp) {
  182. if ($this->isLink() && function_exists('lchgrp')) {
  183. return @lchgrp($this->path, $grp);
  184. } else {
  185. return @chgrp($this->path, $grp);
  186. }
  187. }
  188. /**
  189. * Sets the owner for the fs resource
  190. *
  191. * @param mixed $owner
  192. * @return boolean True if successful
  193. */
  194. public function chown($owner) {
  195. if ($this->isLink() && function_exists('lchown')) {
  196. return @lchown($this->path, $owner);
  197. } else {
  198. return @chown($this->path, $owner);
  199. }
  200. }
  201. /**
  202. * Check to see if the fs resource exists
  203. *
  204. * @return boolean True if exists
  205. */
  206. public function exists() {
  207. return file_exists($this->path);
  208. }
  209. /**
  210. * Check to see if the fs resource is readable
  211. *
  212. * @return boolean True if readable
  213. */
  214. public function isReadable() {
  215. return is_readable($this->path);
  216. }
  217. /**
  218. * Check to see if the fs resource is writable
  219. *
  220. * @return boolean True if writable
  221. */
  222. public function isWritable() {
  223. return is_writable($this->path);
  224. }
  225. /**
  226. * Check to see if fs resource is symlink
  227. *
  228. * @return boolean True if symlink
  229. */
  230. public function isLink() {
  231. return is_link($this->path);
  232. }
  233. /**
  234. * Gets the permission group for the fs resource
  235. *
  236. * @return string The group name of the fs resource
  237. */
  238. public function getGroup() {
  239. return filegroup($this->path);
  240. }
  241. /**
  242. * Alias for chgrp
  243. *
  244. * @see chgrp
  245. */
  246. public function setGroup($grp) {
  247. return $this->chgrp($grp);
  248. }
  249. /**
  250. * Renames the file/folder
  251. *
  252. * @param string $newPath The new path for the fs resource
  253. * @return boolean True if successful
  254. */
  255. public function rename($newPath) {
  256. $newPath = $this->fileHandler->sanitizePath($newPath);
  257. if (!$this->isWritable()) return false;
  258. if (file_exists($newPath)) return false;
  259. return @rename($this->path, $newPath);
  260. }
  261. /**
  262. * Parses a string mode into octal format
  263. *
  264. * @param string $mode The octal to parse
  265. * @return octal The new mode in octal format
  266. */
  267. protected function parseMode($mode = '') {
  268. return octdec($mode);
  269. }
  270. /**
  271. * Gets the parent containing directory of this fs resource
  272. *
  273. * @param <type> $raw Whether or not to return a modDirectory or string path
  274. * @return modDirectory/string Returns either a modDirectory object of the
  275. * parent directory, or the absolute path of the parent, depending on
  276. * whether or not $raw is set to true.
  277. */
  278. public function getParentDirectory($raw = false) {
  279. $ppath = dirname($this->path) . '/';
  280. $ppath = str_replace('//', '/', $ppath);
  281. if ($raw) return $ppath;
  282. $directory = $this->fileHandler->make($ppath,array(),'modDirectory');
  283. return $directory;
  284. }
  285. }
  286. /**
  287. * File implementation of modFileSystemResource
  288. */
  289. class modFile extends modFileSystemResource {
  290. /**
  291. * @var string The content of the resource
  292. */
  293. protected $content = '';
  294. /**
  295. * @see modFileSystemResource.parseMode
  296. */
  297. protected function parseMode($mode = '') {
  298. if (empty($mode)) $mode = $this->fileHandler->context->getOption('new_file_permissions', '0644', $this->fileHandler->config);
  299. return parent::parseMode($mode);
  300. }
  301. /**
  302. * Actually create the file on the file system
  303. *
  304. * @param string $content The content of the file to write
  305. * @param string $mode The perms to write with the file
  306. * @return boolean True if successful
  307. */
  308. public function create($content = '', $mode = 'w+') {
  309. if ($this->exists()) return false;
  310. $result = false;
  311. $fp = @fopen($this->path, 'w+');
  312. if ($fp) {
  313. $result = @fwrite($fp, $content);
  314. @fclose($fp);
  315. }
  316. return $result;
  317. }
  318. /**
  319. * Temporarly set (but not save) the content of the file
  320. * @param string $content The content
  321. */
  322. public function setContent($content) {
  323. $this->content = $content;
  324. }
  325. /**
  326. * Get the contents of the file
  327. *
  328. * @return string The contents of the file
  329. */
  330. public function getContents() {
  331. return @file_get_contents($this->path);
  332. }
  333. /**
  334. * Alias for save()
  335. *
  336. * @see modDirectory::write
  337. */
  338. public function write($content = null, $mode = 'w+') {
  339. return $this->save($content, $mode);
  340. }
  341. /**
  342. * Writes the content of the modFile object to the actual file.
  343. *
  344. * @param string $content Optional. If not using setContent, this will set
  345. * the content to write.
  346. * @return mixed The result of the fwrite
  347. */
  348. public function save($content = null, $mode = 'w+') {
  349. if ($content !== null) $this->content = $content;
  350. $result = false;
  351. $fp = @fopen($this->path, $mode);
  352. if ($fp) {
  353. $result = @fwrite($fp, $this->content);
  354. @fclose($fp);
  355. }
  356. return $result;
  357. }
  358. /**
  359. * Gets the size of the file
  360. *
  361. * @return int The size of the file, in bytes
  362. */
  363. public function getSize() {
  364. return filesize($this->path);
  365. }
  366. /**
  367. * Gets the last accessed time of the file
  368. *
  369. * @param string $timeFormat The format, in strftime format, of the time
  370. * @return string The formatted time
  371. */
  372. public function getLastAccessed($timeFormat = '%b %d, %Y %H:%I:%S %p') {
  373. return strftime($timeFormat, fileatime($this->path));
  374. }
  375. /**
  376. * Gets the last modified time of the file
  377. *
  378. * @param string $timeFormat The format, in strftime format, of the time
  379. * @return string The formatted time
  380. */
  381. public function getLastModified($timeFormat = '%b %d, %Y %H:%I:%S %p') {
  382. return strftime($timeFormat, filemtime($this->path));
  383. }
  384. /**
  385. * Gets the file extension of the file
  386. *
  387. * @return string The file extension of the file
  388. */
  389. public function getExtension() {
  390. return pathinfo($this->path, PATHINFO_EXTENSION);
  391. }
  392. /**
  393. * Gets the basename, or only the filename without the path, of the file
  394. *
  395. * @return string The basename of the file
  396. */
  397. public function getBaseName() {
  398. return ltrim(strrchr($this->path, '/'), '/');
  399. }
  400. /**
  401. * Deletes the file from the filesystem
  402. *
  403. * @return boolean True if successful
  404. */
  405. public function remove() {
  406. if (!$this->exists()) return false;
  407. return @unlink($this->path);
  408. }
  409. }
  410. /**
  411. * Representation of a directory
  412. */
  413. class modDirectory extends modFileSystemResource {
  414. /**
  415. * Actually creates the new directory on the file system.
  416. *
  417. * @param string $mode Optional. The permissions of the new directory.
  418. * @return boolean True if successful
  419. */
  420. public function create($mode = '') {
  421. $mode = $this->parseMode($mode);
  422. if ($this->exists()) return false;
  423. return $this->fileHandler->modx->cacheManager->writeTree($this->path);
  424. }
  425. /**
  426. * @see modFileSystemResource::parseMode
  427. */
  428. protected function parseMode($mode = '') {
  429. if (empty($mode)) $mode = $this->fileHandler->context->getOption('new_folder_permissions', '0755', $this->fileHandler->config);
  430. return parent::parseMode($mode);
  431. }
  432. /**
  433. * Removes the directory from the file system, recursively removing
  434. * subdirectories and files.
  435. *
  436. * @param array $options Options for removal.
  437. * @return boolean True if successful
  438. */
  439. public function remove($options = array()) {
  440. if ($this->path == '/') return false;
  441. $options = array_merge(array(
  442. 'deleteTop' => true,
  443. 'skipDirs' => false,
  444. 'extensions' => '',
  445. ), $options);
  446. $this->fileHandler->modx->getCacheManager();
  447. return $this->fileHandler->modx->cacheManager->deleteTree($this->path, $options);
  448. }
  449. }