PageRenderTime 27ms CodeModel.GetById 33ms RepoModel.GetById 0ms app.codeStats 1ms

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

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