PageRenderTime 30ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

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

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