PageRenderTime 54ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/libraries/joomla/cache/storage/file.php

https://github.com/rietn/minima
PHP | 580 lines | 316 code | 79 blank | 185 comment | 91 complexity | 6c97a0685bcec4a91e5a34070a9cf479 MD5 | raw file
  1. <?php
  2. /**
  3. * @version $Id: file.php 20228 2011-01-10 00:52:54Z eddieajau $
  4. * @package Joomla.Framework
  5. * @subpackage Cache
  6. * @copyright Copyright (C) 2005 - 2011 Open Source Matters, Inc. All rights reserved.
  7. * @license GNU General Public License version 2 or later; see LICENSE.txt
  8. */
  9. // No direct access
  10. defined('JPATH_BASE') or die;
  11. /**
  12. * File cache storage handler
  13. *
  14. * @package Joomla.Framework
  15. * @subpackage Cache
  16. * @since 1.5
  17. */
  18. class JCacheStorageFile extends JCacheStorage
  19. {
  20. /**
  21. * @since 1.6
  22. */
  23. private $_root;
  24. /**
  25. * Constructor
  26. *
  27. * @param array $options optional parameters
  28. * @since 1.5
  29. */
  30. public function __construct($options = array())
  31. {
  32. parent::__construct($options);
  33. $this->_root = $options['cachebase'];
  34. }
  35. // NOTE: raw php calls are up to 100 times faster than JFile or JFolder
  36. /**
  37. * Get cached data from a file by id and group
  38. *
  39. * @param string $id The cache data id
  40. * @param string $group The cache data group
  41. * @param boolean $checkTime True to verify cache time expiration threshold
  42. * @return mixed Boolean false on failure or a cached data string
  43. * @since 1.5
  44. */
  45. public function get($id, $group, $checkTime = true)
  46. {
  47. $data = false;
  48. $path = $this->_getFilePath($id, $group);
  49. if ($checkTime == false || ($checkTime == true && $this->_checkExpire($id, $group) === true)) {
  50. if (file_exists($path)) {
  51. $data = file_get_contents($path);
  52. if ($data) {
  53. // Remove the initial die() statement
  54. $data = str_replace('<?php die("Access Denied"); ?>#x#', '', $data);
  55. }
  56. }
  57. return $data;
  58. } else {
  59. return false;
  60. }
  61. }
  62. /**
  63. * Get all cached data
  64. *
  65. * @return array data
  66. * @since 1.6
  67. */
  68. public function getAll()
  69. {
  70. parent::getAll();
  71. $path = $this->_root;
  72. $folders = $this->_folders($path);
  73. $data = array();
  74. foreach ($folders as $folder) {
  75. $files = array();
  76. $files = $this->_filesInFolder($path.DS.$folder);
  77. $item = new JCacheStorageHelper($folder);
  78. foreach ($files as $file) {
  79. $item->updateSize(filesize($path.DS.$folder.DS.$file) / 1024);
  80. }
  81. $data[$folder] = $item;
  82. }
  83. return $data;
  84. }
  85. /**
  86. * Store the data to a file by id and group
  87. *
  88. * @param string $id The cache data id
  89. * @param string $group The cache data group
  90. * @param string $data The data to store in cache
  91. * @return boolean True on success, false otherwise
  92. * @since 1.5
  93. */
  94. public function store($id, $group, $data)
  95. {
  96. $written = false;
  97. $path = $this->_getFilePath($id, $group);
  98. $die = '<?php die("Access Denied"); ?>#x#';
  99. // Prepend a die string
  100. $data = $die.$data;
  101. $_fileopen = @fopen($path, "wb");
  102. if ($_fileopen) {
  103. $len = strlen($data);
  104. @fwrite($_fileopen, $data, $len);
  105. $written = true;
  106. }
  107. // Data integrity check
  108. if ($written && ($data == file_get_contents($path))) {
  109. return true;
  110. } else {
  111. return false;
  112. }
  113. }
  114. /**
  115. * Remove a cached data file by id and group
  116. *
  117. * @param string $id The cache data id
  118. * @param string $group The cache data group
  119. * @return boolean True on success, false otherwise
  120. * @since 1.5
  121. */
  122. public function remove($id, $group)
  123. {
  124. $path = $this->_getFilePath($id, $group);
  125. if (!@unlink($path)) {
  126. return false;
  127. }
  128. return true;
  129. }
  130. /**
  131. * Clean cache for a group given a mode.
  132. *
  133. * group mode : cleans all cache in the group
  134. * notgroup mode : cleans all cache not in the group
  135. *
  136. * @param string $group The cache data group
  137. * @param string $mode The mode for cleaning cache [group|notgroup]
  138. * @return boolean True on success, false otherwise
  139. * @since 1.5
  140. */
  141. public function clean($group, $mode = null)
  142. {
  143. $return = true;
  144. $folder = $group;
  145. if (trim($folder) == '') {
  146. $mode = 'notgroup';
  147. }
  148. switch ($mode) {
  149. case 'notgroup':
  150. $folders = $this->_folders($this->_root);
  151. for ($i=0, $n=count($folders); $i<$n; $i++) {
  152. if ($folders[$i] != $folder) {
  153. $return |= $this->_deleteFolder($this->_root.DS.$folders[$i]);
  154. }
  155. }
  156. break;
  157. case 'group':
  158. default:
  159. if (is_dir($this->_root.DS.$folder)) {
  160. $return = $this->_deleteFolder($this->_root.DS.$folder);
  161. }
  162. break;
  163. }
  164. return $return;
  165. }
  166. /**
  167. * Garbage collect expired cache data
  168. *
  169. * @return boolean True on success, false otherwise.
  170. * @since 1.5
  171. */
  172. public function gc()
  173. {
  174. $result = true;
  175. // files older than lifeTime get deleted from cache
  176. $files = $this->_filesInFolder($this->_root, '', true, true);
  177. foreach($files As $file) {
  178. $time = @filemtime($file);
  179. if (($time + $this->_lifetime) < $this->_now || empty($time)) {
  180. $result |= @unlink($file);
  181. }
  182. }
  183. return $result;
  184. }
  185. /**
  186. * Test to see if the cache storage is available.
  187. *
  188. * @return boolean True on success, false otherwise.
  189. * @since 1.5
  190. */
  191. public static function test()
  192. {
  193. $conf = JFactory::getConfig();
  194. return is_writable($conf->get('cache_path', JPATH_CACHE));
  195. }
  196. /**
  197. * Lock cached item
  198. *
  199. * @param string $id The cache data id
  200. * @param string $group The cache data group
  201. * @param integer $locktime Cached item max lock time
  202. * @return boolean True on success, false otherwise.
  203. * @since 1.6
  204. */
  205. public function lock($id,$group,$locktime)
  206. {
  207. $returning = new stdClass();
  208. $returning->locklooped = false;
  209. $looptime = $locktime * 10;
  210. $path = $this->_getFilePath($id, $group);
  211. $_fileopen = @fopen($path, "r+b");
  212. if ($_fileopen) {
  213. $data_lock = @flock($_fileopen, LOCK_EX);
  214. } else {
  215. $data_lock = false;
  216. }
  217. if ($data_lock === false) {
  218. $lock_counter = 0;
  219. // loop until you find that the lock has been released. that implies that data get from other thread has finished
  220. while ($data_lock === false) {
  221. if ($lock_counter > $looptime) {
  222. $returning->locked = false;
  223. $returning->locklooped = true;
  224. break;
  225. }
  226. usleep(100);
  227. $data_lock = @flock($_fileopen, LOCK_EX);
  228. $lock_counter++;
  229. }
  230. }
  231. $returning->locked = $data_lock;
  232. return $returning;
  233. }
  234. /**
  235. * Unlock cached item
  236. *
  237. * @param string $id The cache data id
  238. * @param string $group The cache data group
  239. * @return boolean True on success, false otherwise.
  240. * @since 1.6
  241. */
  242. public function unlock($id, $group = null)
  243. {
  244. $path = $this->_getFilePath($id, $group);
  245. $_fileopen = @fopen($path, "r+b");
  246. if ($_fileopen) {
  247. $ret = @flock($_fileopen, LOCK_UN);
  248. @fclose($_fileopen);
  249. }
  250. return $ret;
  251. }
  252. /**
  253. * Check to make sure cache is still valid, if not, delete it.
  254. *
  255. * @param string $id Cache key to expire.
  256. * @param string $group The cache data group.
  257. * @since 1.6
  258. */
  259. private function _checkExpire($id, $group)
  260. {
  261. $path = $this->_getFilePath($id, $group);
  262. // check prune period
  263. if (file_exists($path)) {
  264. $time = @filemtime($path);
  265. if (($time + $this->_lifetime) < $this->_now || empty($time)) {
  266. @unlink($path);
  267. return false;
  268. }
  269. return true;
  270. }
  271. return false;
  272. }
  273. /**
  274. * Get a cache file path from an id/group pair
  275. *
  276. * @param string $id The cache data id
  277. * @param string $group The cache data group
  278. * @return string The cache file path
  279. * @since 1.5
  280. */
  281. private function _getFilePath($id, $group)
  282. {
  283. $name = $this->_getCacheId($id, $group);
  284. $dir = $this->_root.DS.$group;
  285. // If the folder doesn't exist try to create it
  286. if (!is_dir($dir)) {
  287. // Make sure the index file is there
  288. $indexFile = $dir.'/index.html';
  289. @ mkdir($dir) && file_put_contents($indexFile, '<html><body bgcolor="#FFFFFF"></body></html>');
  290. }
  291. // Make sure the folder exists
  292. if (!is_dir($dir)) {
  293. return false;
  294. }
  295. return $dir.DS.$name.'.php';
  296. }
  297. /**
  298. * Quickly delete a folder of files
  299. *
  300. * @param string The path to the folder to delete.
  301. * @return boolean True on success.
  302. * @since 1.6
  303. */
  304. private function _deleteFolder($path)
  305. {
  306. // Sanity check
  307. if (!$path || !is_dir($path) || empty($this->_root)) {
  308. // Bad programmer! Bad Bad programmer!
  309. JError::raiseWarning(500, 'JCacheStorageFile::_deleteFolder ' . JText::_('JLIB_FILESYSTEM_ERROR_DELETE_BASE_DIRECTORY'));
  310. return false;
  311. }
  312. $path = $this->_cleanPath($path);
  313. // Check to make sure path is inside cache folder, we do not want to delete Joomla root!
  314. $pos = strpos($path, $this->_cleanPath($this->_root));
  315. if ($pos === false || $pos > 0) {
  316. JError::raiseWarning(500, 'JCacheStorageFile::_deleteFolder' . JText::sprintf('JLIB_FILESYSTEM_ERROR_PATH_IS_NOT_A_FOLDER', $path));
  317. return false;
  318. }
  319. // Remove all the files in folder if they exist; disable all filtering
  320. $files = $this->_filesInFolder($path, '.', false, true, array(), array());
  321. if (!empty($files) && !is_array($files)) {
  322. if (@unlink($files) !== true) {
  323. return false;
  324. }
  325. } else if (!empty($files) && is_array($files)) {
  326. foreach ($files as $file)
  327. {
  328. $file = $this->_cleanPath($file);
  329. // In case of restricted permissions we zap it one way or the other
  330. // as long as the owner is either the webserver or the ftp
  331. if (@unlink($file)) {
  332. // Do nothing
  333. } else {
  334. $filename = basename($file);
  335. JError::raiseWarning('SOME_ERROR_CODE', 'JCacheStorageFile::_deleteFolder' . JText::sprintf('JLIB_FILESYSTEM_DELETE_FAILED', $filename));
  336. return false;
  337. }
  338. }
  339. }
  340. // Remove sub-folders of folder; disable all filtering
  341. $folders = $this->_folders($path, '.', false, true, array(), array());
  342. foreach ($folders as $folder) {
  343. if (is_link($folder)) {
  344. // Don't descend into linked directories, just delete the link.
  345. if (@unlink($folder) !== true) {
  346. return false;
  347. }
  348. } elseif ($this->_deleteFolder($folder) !== true) {
  349. return false;
  350. }
  351. }
  352. // In case of restricted permissions we zap it one way or the other
  353. // as long as the owner is either the webserver or the ftp
  354. if (@rmdir($path)) {
  355. $ret = true;
  356. } else {
  357. JError::raiseWarning('SOME_ERROR_CODE', 'JCacheStorageFile::_deleteFolder' . JText::sprintf('JLIB_FILESYSTEM_ERROR_FOLDER_DELETE', $path));
  358. $ret = false;
  359. }
  360. return $ret;
  361. }
  362. /**
  363. * Function to strip additional / or \ in a path name
  364. *
  365. * @param string The path to clean
  366. * @param string Directory separator (optional)
  367. * @return string The cleaned path
  368. * @since 1.6
  369. */
  370. private function _cleanPath($path, $ds = DS)
  371. {
  372. $path = trim($path);
  373. if (empty($path)) {
  374. $path = $this->_root;
  375. } else {
  376. // Remove double slashes and backslahses and convert all slashes and backslashes to DS
  377. $path = preg_replace('#[/\\\\]+#', $ds, $path);
  378. }
  379. return $path;
  380. }
  381. /**
  382. * Utility function to quickly read the files in a folder.
  383. *
  384. * @param string The path of the folder to read.
  385. * @param string A filter for file names.
  386. * @param mixed True to recursively search into sub-folders, or an
  387. * integer to specify the maximum depth.
  388. * @param boolean True to return the full path to the file.
  389. * @param array Array with names of files which should not be shown in
  390. * the result.
  391. * @return array Files in the given folder.
  392. * @since 1.6
  393. */
  394. private function _filesInFolder($path, $filter = '.', $recurse = false, $fullpath = false, $exclude = array('.svn', 'CVS','.DS_Store','__MACOSX'), $excludefilter = array('^\..*','.*~'))
  395. {
  396. // Initialise variables.
  397. $arr = array();
  398. // Check to make sure the path valid and clean
  399. $path = $this->_cleanPath($path);
  400. // Is the path a folder?
  401. if (!is_dir($path)) {
  402. JError::raiseWarning(21, 'JCacheStorageFile::_filesInFolder' . JText::sprintf('JLIB_FILESYSTEM_ERROR_PATH_IS_NOT_A_FOLDER', $path));
  403. return false;
  404. }
  405. // read the source directory
  406. $handle = opendir($path);
  407. if (count($excludefilter)) {
  408. $excludefilter = '/('. implode('|', $excludefilter) .')/';
  409. } else {
  410. $excludefilter = '';
  411. }
  412. while (($file = readdir($handle)) !== false)
  413. {
  414. if (($file != '.') && ($file != '..') && (!in_array($file, $exclude)) && (!$excludefilter || !preg_match($excludefilter, $file))) {
  415. $dir = $path . DS . $file;
  416. $isDir = is_dir($dir);
  417. if ($isDir) {
  418. if ($recurse) {
  419. if (is_integer($recurse)) {
  420. $arr2 = $this->_filesInFolder($dir, $filter, $recurse - 1, $fullpath);
  421. } else {
  422. $arr2 = $this->_filesInFolder($dir, $filter, $recurse, $fullpath);
  423. }
  424. $arr = array_merge($arr, $arr2);
  425. }
  426. } else {
  427. if (preg_match("/$filter/", $file)) {
  428. if ($fullpath) {
  429. $arr[] = $path . DS . $file;
  430. } else {
  431. $arr[] = $file;
  432. }
  433. }
  434. }
  435. }
  436. }
  437. closedir($handle);
  438. return $arr;
  439. }
  440. /**
  441. * Utility function to read the folders in a folder.
  442. *
  443. * @param string The path of the folder to read.
  444. * @param string A filter for folder names.
  445. * @param mixed True to recursively search into sub-folders, or an
  446. * integer to specify the maximum depth.
  447. * @param boolean True to return the full path to the folders.
  448. * @param array Array with names of folders which should not be shown in
  449. * the result.
  450. * @param array Array with regular expressions matching folders which
  451. * should not be shown in the result.
  452. * @return array Folders in the given folder.
  453. * @since 1.6
  454. */
  455. private function _folders($path, $filter = '.', $recurse = false, $fullpath = false, $exclude = array('.svn', 'CVS','.DS_Store','__MACOSX'), $excludefilter = array('^\..*'))
  456. {
  457. // Initialise variables.
  458. $arr = array();
  459. // Check to make sure the path valid and clean
  460. $path = $this->_cleanPath($path);
  461. // Is the path a folder?
  462. if (!is_dir($path)) {
  463. JError::raiseWarning(21, 'JCacheStorageFile::_folders' . JText::sprintf('JLIB_FILESYSTEM_ERROR_PATH_IS_NOT_A_FOLDER', $path));
  464. return false;
  465. }
  466. // read the source directory
  467. $handle = opendir($path);
  468. if (count($excludefilter)) {
  469. $excludefilter_string = '/('. implode('|', $excludefilter) .')/';
  470. } else {
  471. $excludefilter_string = '';
  472. }
  473. while (($file = readdir($handle)) !== false)
  474. {
  475. if (($file != '.') && ($file != '..') && (!in_array($file, $exclude)) && (empty($excludefilter_string) || !preg_match($excludefilter_string, $file))) {
  476. $dir = $path . DS . $file;
  477. $isDir = is_dir($dir);
  478. if ($isDir) {
  479. // Removes filtered directories
  480. if (preg_match("/$filter/", $file)) {
  481. if ($fullpath) {
  482. $arr[] = $dir;
  483. } else {
  484. $arr[] = $file;
  485. }
  486. }
  487. if ($recurse) {
  488. if (is_integer($recurse)) {
  489. $arr2 = $this->_folders($dir, $filter, $recurse - 1, $fullpath, $exclude, $excludefilter);
  490. } else {
  491. $arr2 = $this->_folders($dir, $filter, $recurse, $fullpath, $exclude, $excludefilter);
  492. }
  493. $arr = array_merge($arr, $arr2);
  494. }
  495. }
  496. }
  497. }
  498. closedir($handle);
  499. return $arr;
  500. }
  501. }