PageRenderTime 57ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/src/lib/Zend/Cache/Backend/File.php

https://bitbucket.org/mkrasuski/magento-ce
PHP | 1037 lines | 674 code | 38 blank | 325 comment | 63 complexity | 292986e780e4b420968b5dfa15267a01 MD5 | raw file
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Cache
  17. * @subpackage Zend_Cache_Backend
  18. * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
  19. * @license http://framework.zend.com/license/new-bsd New BSD License
  20. * @version $Id$
  21. */
  22. /**
  23. * @see Zend_Cache_Backend_Interface
  24. */
  25. #require_once 'Zend/Cache/Backend/ExtendedInterface.php';
  26. /**
  27. * @see Zend_Cache_Backend
  28. */
  29. #require_once 'Zend/Cache/Backend.php';
  30. /**
  31. * @package Zend_Cache
  32. * @subpackage Zend_Cache_Backend
  33. * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
  34. * @license http://framework.zend.com/license/new-bsd New BSD License
  35. */
  36. class Zend_Cache_Backend_File extends Zend_Cache_Backend implements Zend_Cache_Backend_ExtendedInterface
  37. {
  38. /**
  39. * Available options
  40. *
  41. * =====> (string) cache_dir :
  42. * - Directory where to put the cache files
  43. *
  44. * =====> (boolean) file_locking :
  45. * - Enable / disable file_locking
  46. * - Can avoid cache corruption under bad circumstances but it doesn't work on multithread
  47. * webservers and on NFS filesystems for example
  48. *
  49. * =====> (boolean) read_control :
  50. * - Enable / disable read control
  51. * - If enabled, a control key is embeded in cache file and this key is compared with the one
  52. * calculated after the reading.
  53. *
  54. * =====> (string) read_control_type :
  55. * - Type of read control (only if read control is enabled). Available values are :
  56. * 'md5' for a md5 hash control (best but slowest)
  57. * 'crc32' for a crc32 hash control (lightly less safe but faster, better choice)
  58. * 'adler32' for an adler32 hash control (excellent choice too, faster than crc32)
  59. * 'strlen' for a length only test (fastest)
  60. *
  61. * =====> (int) hashed_directory_level :
  62. * - Hashed directory level
  63. * - Set the hashed directory structure level. 0 means "no hashed directory
  64. * structure", 1 means "one level of directory", 2 means "two levels"...
  65. * This option can speed up the cache only when you have many thousands of
  66. * cache file. Only specific benchs can help you to choose the perfect value
  67. * for you. Maybe, 1 or 2 is a good start.
  68. *
  69. * =====> (int) hashed_directory_umask :
  70. * - deprecated
  71. * - Permissions for hashed directory structure
  72. *
  73. * =====> (int) hashed_directory_perm :
  74. * - Permissions for hashed directory structure
  75. *
  76. * =====> (string) file_name_prefix :
  77. * - prefix for cache files
  78. * - be really carefull with this option because a too generic value in a system cache dir
  79. * (like /tmp) can cause disasters when cleaning the cache
  80. *
  81. * =====> (int) cache_file_umask :
  82. * - deprecated
  83. * - Permissions for cache files
  84. *
  85. * =====> (int) cache_file_perm :
  86. * - Permissions for cache files
  87. *
  88. * =====> (int) metatadatas_array_max_size :
  89. * - max size for the metadatas array (don't change this value unless you
  90. * know what you are doing)
  91. *
  92. * @var array available options
  93. */
  94. protected $_options = array(
  95. 'cache_dir' => null,
  96. 'file_locking' => true,
  97. 'read_control' => true,
  98. 'read_control_type' => 'crc32',
  99. 'hashed_directory_level' => 0,
  100. 'hashed_directory_perm' => 0700,
  101. 'file_name_prefix' => 'zend_cache',
  102. 'cache_file_perm' => 0600,
  103. 'metadatas_array_max_size' => 100
  104. );
  105. /**
  106. * Array of metadatas (each item is an associative array)
  107. *
  108. * @var array
  109. */
  110. protected $_metadatasArray = array();
  111. /**
  112. * Constructor
  113. *
  114. * @param array $options associative array of options
  115. * @throws Zend_Cache_Exception
  116. */
  117. public function __construct(array $options = array())
  118. {
  119. parent::__construct($options);
  120. if ($this->_options['cache_dir'] !== null) { // particular case for this option
  121. $this->setCacheDir($this->_options['cache_dir']);
  122. } else {
  123. $this->setCacheDir(self::getTmpDir() . DIRECTORY_SEPARATOR, false);
  124. }
  125. if (isset($this->_options['file_name_prefix'])) { // particular case for this option
  126. if (!preg_match('~^[a-zA-Z0-9_]+$~D', $this->_options['file_name_prefix'])) {
  127. Zend_Cache::throwException('Invalid file_name_prefix : must use only [a-zA-Z0-9_]');
  128. }
  129. }
  130. if ($this->_options['metadatas_array_max_size'] < 10) {
  131. Zend_Cache::throwException('Invalid metadatas_array_max_size, must be > 10');
  132. }
  133. if (isset($options['hashed_directory_umask'])) {
  134. // See #ZF-12047
  135. trigger_error("'hashed_directory_umask' is deprecated -> please use 'hashed_directory_perm' instead", E_USER_NOTICE);
  136. if (!isset($options['hashed_directory_perm'])) {
  137. $options['hashed_directory_perm'] = $options['hashed_directory_umask'];
  138. }
  139. }
  140. if (isset($options['hashed_directory_perm']) && is_string($options['hashed_directory_perm'])) {
  141. // See #ZF-4422
  142. $this->_options['hashed_directory_perm'] = octdec($this->_options['hashed_directory_perm']);
  143. }
  144. if (isset($options['cache_file_umask'])) {
  145. // See #ZF-12047
  146. trigger_error("'cache_file_umask' is deprecated -> please use 'cache_file_perm' instead", E_USER_NOTICE);
  147. if (!isset($options['cache_file_perm'])) {
  148. $options['cache_file_perm'] = $options['cache_file_umask'];
  149. }
  150. }
  151. if (isset($options['cache_file_perm']) && is_string($options['cache_file_perm'])) {
  152. // See #ZF-4422
  153. $this->_options['cache_file_perm'] = octdec($this->_options['cache_file_perm']);
  154. }
  155. }
  156. /**
  157. * Set the cache_dir (particular case of setOption() method)
  158. *
  159. * @param string $value
  160. * @param boolean $trailingSeparator If true, add a trailing separator is necessary
  161. * @throws Zend_Cache_Exception
  162. * @return void
  163. */
  164. public function setCacheDir($value, $trailingSeparator = true)
  165. {
  166. if (!is_dir($value)) {
  167. Zend_Cache::throwException(sprintf('cache_dir "%s" must be a directory', $value));
  168. }
  169. if (!is_writable($value)) {
  170. Zend_Cache::throwException(sprintf('cache_dir "%s" is not writable', $value));
  171. }
  172. if ($trailingSeparator) {
  173. // add a trailing DIRECTORY_SEPARATOR if necessary
  174. $value = rtrim(realpath($value), '\\/') . DIRECTORY_SEPARATOR;
  175. }
  176. $this->_options['cache_dir'] = $value;
  177. }
  178. /**
  179. * Test if a cache is available for the given id and (if yes) return it (false else)
  180. *
  181. * @param string $id cache id
  182. * @param boolean $doNotTestCacheValidity if set to true, the cache validity won't be tested
  183. * @return string|false cached datas
  184. */
  185. public function load($id, $doNotTestCacheValidity = false)
  186. {
  187. if (!($this->_test($id, $doNotTestCacheValidity))) {
  188. // The cache is not hit !
  189. return false;
  190. }
  191. $metadatas = $this->_getMetadatas($id);
  192. $file = $this->_file($id);
  193. $data = $this->_fileGetContents($file);
  194. if ($this->_options['read_control']) {
  195. $hashData = $this->_hash($data, $this->_options['read_control_type']);
  196. $hashControl = $metadatas['hash'];
  197. if ($hashData != $hashControl) {
  198. // Problem detected by the read control !
  199. $this->_log('Zend_Cache_Backend_File::load() / read_control : stored hash and computed hash do not match');
  200. $this->remove($id);
  201. return false;
  202. }
  203. }
  204. return $data;
  205. }
  206. /**
  207. * Test if a cache is available or not (for the given id)
  208. *
  209. * @param string $id cache id
  210. * @return mixed false (a cache is not available) or "last modified" timestamp (int) of the available cache record
  211. */
  212. public function test($id)
  213. {
  214. clearstatcache();
  215. return $this->_test($id, false);
  216. }
  217. /**
  218. * Save some string datas into a cache record
  219. *
  220. * Note : $data is always "string" (serialization is done by the
  221. * core not by the backend)
  222. *
  223. * @param string $data Datas to cache
  224. * @param string $id Cache id
  225. * @param array $tags Array of strings, the cache record will be tagged by each string entry
  226. * @param boolean|int $specificLifetime If != false, set a specific lifetime for this cache record (null => infinite lifetime)
  227. * @return boolean true if no problem
  228. */
  229. public function save($data, $id, $tags = array(), $specificLifetime = false)
  230. {
  231. clearstatcache();
  232. $file = $this->_file($id);
  233. $path = $this->_path($id);
  234. if ($this->_options['hashed_directory_level'] > 0) {
  235. if (!is_writable($path)) {
  236. // maybe, we just have to build the directory structure
  237. $this->_recursiveMkdirAndChmod($id);
  238. }
  239. if (!is_writable($path)) {
  240. return false;
  241. }
  242. }
  243. if ($this->_options['read_control']) {
  244. $hash = $this->_hash($data, $this->_options['read_control_type']);
  245. } else {
  246. $hash = '';
  247. }
  248. $metadatas = array(
  249. 'hash' => $hash,
  250. 'mtime' => time(),
  251. 'expire' => $this->_expireTime($this->getLifetime($specificLifetime)),
  252. 'tags' => $tags
  253. );
  254. $res = $this->_setMetadatas($id, $metadatas);
  255. if (!$res) {
  256. $this->_log('Zend_Cache_Backend_File::save() / error on saving metadata');
  257. return false;
  258. }
  259. $res = $this->_filePutContents($file, $data);
  260. return $res;
  261. }
  262. /**
  263. * Remove a cache record
  264. *
  265. * @param string $id cache id
  266. * @return boolean true if no problem
  267. */
  268. public function remove($id)
  269. {
  270. $file = $this->_file($id);
  271. $boolRemove = $this->_remove($file);
  272. $boolMetadata = $this->_delMetadatas($id);
  273. return $boolMetadata && $boolRemove;
  274. }
  275. /**
  276. * Clean some cache records
  277. *
  278. * Available modes are :
  279. *
  280. * Zend_Cache::CLEANING_MODE_ALL (default) => remove all cache entries ($tags is not used)
  281. * Zend_Cache::CLEANING_MODE_OLD => remove too old cache entries ($tags is not used)
  282. * Zend_Cache::CLEANING_MODE_MATCHING_TAG => remove cache entries matching all given tags
  283. * ($tags can be an array of strings or a single string)
  284. * Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG => remove cache entries not {matching one of the given tags}
  285. * ($tags can be an array of strings or a single string)
  286. * Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG => remove cache entries matching any given tags
  287. * ($tags can be an array of strings or a single string)
  288. *
  289. * @param string $mode clean mode
  290. * @param array $tags array of tags
  291. * @return boolean true if no problem
  292. */
  293. public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array())
  294. {
  295. // We use this protected method to hide the recursive stuff
  296. clearstatcache();
  297. return $this->_clean($this->_options['cache_dir'], $mode, $tags);
  298. }
  299. /**
  300. * Return an array of stored cache ids
  301. *
  302. * @return array array of stored cache ids (string)
  303. */
  304. public function getIds()
  305. {
  306. return $this->_get($this->_options['cache_dir'], 'ids', array());
  307. }
  308. /**
  309. * Return an array of stored tags
  310. *
  311. * @return array array of stored tags (string)
  312. */
  313. public function getTags()
  314. {
  315. return $this->_get($this->_options['cache_dir'], 'tags', array());
  316. }
  317. /**
  318. * Return an array of stored cache ids which match given tags
  319. *
  320. * In case of multiple tags, a logical AND is made between tags
  321. *
  322. * @param array $tags array of tags
  323. * @return array array of matching cache ids (string)
  324. */
  325. public function getIdsMatchingTags($tags = array())
  326. {
  327. return $this->_get($this->_options['cache_dir'], 'matching', $tags);
  328. }
  329. /**
  330. * Return an array of stored cache ids which don't match given tags
  331. *
  332. * In case of multiple tags, a logical OR is made between tags
  333. *
  334. * @param array $tags array of tags
  335. * @return array array of not matching cache ids (string)
  336. */
  337. public function getIdsNotMatchingTags($tags = array())
  338. {
  339. return $this->_get($this->_options['cache_dir'], 'notMatching', $tags);
  340. }
  341. /**
  342. * Return an array of stored cache ids which match any given tags
  343. *
  344. * In case of multiple tags, a logical AND is made between tags
  345. *
  346. * @param array $tags array of tags
  347. * @return array array of any matching cache ids (string)
  348. */
  349. public function getIdsMatchingAnyTags($tags = array())
  350. {
  351. return $this->_get($this->_options['cache_dir'], 'matchingAny', $tags);
  352. }
  353. /**
  354. * Return the filling percentage of the backend storage
  355. *
  356. * @throws Zend_Cache_Exception
  357. * @return int integer between 0 and 100
  358. */
  359. public function getFillingPercentage()
  360. {
  361. $free = disk_free_space($this->_options['cache_dir']);
  362. $total = disk_total_space($this->_options['cache_dir']);
  363. if ($total == 0) {
  364. Zend_Cache::throwException('can\'t get disk_total_space');
  365. } else {
  366. if ($free >= $total) {
  367. return 100;
  368. }
  369. return ((int) (100. * ($total - $free) / $total));
  370. }
  371. }
  372. /**
  373. * Return an array of metadatas for the given cache id
  374. *
  375. * The array must include these keys :
  376. * - expire : the expire timestamp
  377. * - tags : a string array of tags
  378. * - mtime : timestamp of last modification time
  379. *
  380. * @param string $id cache id
  381. * @return array array of metadatas (false if the cache id is not found)
  382. */
  383. public function getMetadatas($id)
  384. {
  385. $metadatas = $this->_getMetadatas($id);
  386. if (!$metadatas) {
  387. return false;
  388. }
  389. if (time() > $metadatas['expire']) {
  390. return false;
  391. }
  392. return array(
  393. 'expire' => $metadatas['expire'],
  394. 'tags' => $metadatas['tags'],
  395. 'mtime' => $metadatas['mtime']
  396. );
  397. }
  398. /**
  399. * Give (if possible) an extra lifetime to the given cache id
  400. *
  401. * @param string $id cache id
  402. * @param int $extraLifetime
  403. * @return boolean true if ok
  404. */
  405. public function touch($id, $extraLifetime)
  406. {
  407. $metadatas = $this->_getMetadatas($id);
  408. if (!$metadatas) {
  409. return false;
  410. }
  411. if (time() > $metadatas['expire']) {
  412. return false;
  413. }
  414. $newMetadatas = array(
  415. 'hash' => $metadatas['hash'],
  416. 'mtime' => time(),
  417. 'expire' => $metadatas['expire'] + $extraLifetime,
  418. 'tags' => $metadatas['tags']
  419. );
  420. $res = $this->_setMetadatas($id, $newMetadatas);
  421. if (!$res) {
  422. return false;
  423. }
  424. return true;
  425. }
  426. /**
  427. * Return an associative array of capabilities (booleans) of the backend
  428. *
  429. * The array must include these keys :
  430. * - automatic_cleaning (is automating cleaning necessary)
  431. * - tags (are tags supported)
  432. * - expired_read (is it possible to read expired cache records
  433. * (for doNotTestCacheValidity option for example))
  434. * - priority does the backend deal with priority when saving
  435. * - infinite_lifetime (is infinite lifetime can work with this backend)
  436. * - get_list (is it possible to get the list of cache ids and the complete list of tags)
  437. *
  438. * @return array associative of with capabilities
  439. */
  440. public function getCapabilities()
  441. {
  442. return array(
  443. 'automatic_cleaning' => true,
  444. 'tags' => true,
  445. 'expired_read' => true,
  446. 'priority' => false,
  447. 'infinite_lifetime' => true,
  448. 'get_list' => true
  449. );
  450. }
  451. /**
  452. * PUBLIC METHOD FOR UNIT TESTING ONLY !
  453. *
  454. * Force a cache record to expire
  455. *
  456. * @param string $id cache id
  457. */
  458. public function ___expire($id)
  459. {
  460. $metadatas = $this->_getMetadatas($id);
  461. if ($metadatas) {
  462. $metadatas['expire'] = 1;
  463. $this->_setMetadatas($id, $metadatas);
  464. }
  465. }
  466. /**
  467. * Get a metadatas record
  468. *
  469. * @param string $id Cache id
  470. * @return array|false Associative array of metadatas
  471. */
  472. protected function _getMetadatas($id)
  473. {
  474. if (isset($this->_metadatasArray[$id])) {
  475. return $this->_metadatasArray[$id];
  476. } else {
  477. $metadatas = $this->_loadMetadatas($id);
  478. if (!$metadatas) {
  479. return false;
  480. }
  481. $this->_setMetadatas($id, $metadatas, false);
  482. return $metadatas;
  483. }
  484. }
  485. /**
  486. * Set a metadatas record
  487. *
  488. * @param string $id Cache id
  489. * @param array $metadatas Associative array of metadatas
  490. * @param boolean $save optional pass false to disable saving to file
  491. * @return boolean True if no problem
  492. */
  493. protected function _setMetadatas($id, $metadatas, $save = true)
  494. {
  495. if (count($this->_metadatasArray) >= $this->_options['metadatas_array_max_size']) {
  496. $n = (int) ($this->_options['metadatas_array_max_size'] / 10);
  497. $this->_metadatasArray = array_slice($this->_metadatasArray, $n);
  498. }
  499. if ($save) {
  500. $result = $this->_saveMetadatas($id, $metadatas);
  501. if (!$result) {
  502. return false;
  503. }
  504. }
  505. $this->_metadatasArray[$id] = $metadatas;
  506. return true;
  507. }
  508. /**
  509. * Drop a metadata record
  510. *
  511. * @param string $id Cache id
  512. * @return boolean True if no problem
  513. */
  514. protected function _delMetadatas($id)
  515. {
  516. if (isset($this->_metadatasArray[$id])) {
  517. unset($this->_metadatasArray[$id]);
  518. }
  519. $file = $this->_metadatasFile($id);
  520. return $this->_remove($file);
  521. }
  522. /**
  523. * Clear the metadatas array
  524. *
  525. * @return void
  526. */
  527. protected function _cleanMetadatas()
  528. {
  529. $this->_metadatasArray = array();
  530. }
  531. /**
  532. * Load metadatas from disk
  533. *
  534. * @param string $id Cache id
  535. * @return array|false Metadatas associative array
  536. */
  537. protected function _loadMetadatas($id)
  538. {
  539. $file = $this->_metadatasFile($id);
  540. $result = $this->_fileGetContents($file);
  541. if (!$result) {
  542. return false;
  543. }
  544. $tmp = @unserialize($result);
  545. return $tmp;
  546. }
  547. /**
  548. * Save metadatas to disk
  549. *
  550. * @param string $id Cache id
  551. * @param array $metadatas Associative array
  552. * @return boolean True if no problem
  553. */
  554. protected function _saveMetadatas($id, $metadatas)
  555. {
  556. $file = $this->_metadatasFile($id);
  557. $result = $this->_filePutContents($file, serialize($metadatas));
  558. if (!$result) {
  559. return false;
  560. }
  561. return true;
  562. }
  563. /**
  564. * Make and return a file name (with path) for metadatas
  565. *
  566. * @param string $id Cache id
  567. * @return string Metadatas file name (with path)
  568. */
  569. protected function _metadatasFile($id)
  570. {
  571. $path = $this->_path($id);
  572. $fileName = $this->_idToFileName('internal-metadatas---' . $id);
  573. return $path . $fileName;
  574. }
  575. /**
  576. * Check if the given filename is a metadatas one
  577. *
  578. * @param string $fileName File name
  579. * @return boolean True if it's a metadatas one
  580. */
  581. protected function _isMetadatasFile($fileName)
  582. {
  583. $id = $this->_fileNameToId($fileName);
  584. if (substr($id, 0, 21) == 'internal-metadatas---') {
  585. return true;
  586. } else {
  587. return false;
  588. }
  589. }
  590. /**
  591. * Remove a file
  592. *
  593. * If we can't remove the file (because of locks or any problem), we will touch
  594. * the file to invalidate it
  595. *
  596. * @param string $file Complete file path
  597. * @return boolean True if ok
  598. */
  599. protected function _remove($file)
  600. {
  601. if (!is_file($file)) {
  602. return false;
  603. }
  604. if (!@unlink($file)) {
  605. # we can't remove the file (because of locks or any problem)
  606. $this->_log("Zend_Cache_Backend_File::_remove() : we can't remove $file");
  607. return false;
  608. }
  609. return true;
  610. }
  611. /**
  612. * Clean some cache records (protected method used for recursive stuff)
  613. *
  614. * Available modes are :
  615. * Zend_Cache::CLEANING_MODE_ALL (default) => remove all cache entries ($tags is not used)
  616. * Zend_Cache::CLEANING_MODE_OLD => remove too old cache entries ($tags is not used)
  617. * Zend_Cache::CLEANING_MODE_MATCHING_TAG => remove cache entries matching all given tags
  618. * ($tags can be an array of strings or a single string)
  619. * Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG => remove cache entries not {matching one of the given tags}
  620. * ($tags can be an array of strings or a single string)
  621. * Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG => remove cache entries matching any given tags
  622. * ($tags can be an array of strings or a single string)
  623. *
  624. * @param string $dir Directory to clean
  625. * @param string $mode Clean mode
  626. * @param array $tags Array of tags
  627. * @throws Zend_Cache_Exception
  628. * @return boolean True if no problem
  629. */
  630. protected function _clean($dir, $mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array())
  631. {
  632. if (!is_dir($dir)) {
  633. return false;
  634. }
  635. $result = true;
  636. $prefix = $this->_options['file_name_prefix'];
  637. $glob = @glob($dir . $prefix . '--*');
  638. if ($glob === false) {
  639. // On some systems it is impossible to distinguish between empty match and an error.
  640. return true;
  641. }
  642. $metadataFiles = array();
  643. foreach ($glob as $file) {
  644. if (is_file($file)) {
  645. $fileName = basename($file);
  646. if ($this->_isMetadatasFile($fileName)) {
  647. // In CLEANING_MODE_ALL, we drop anything, even remainings old metadatas files.
  648. // To do that, we need to save the list of the metadata files first.
  649. if ($mode == Zend_Cache::CLEANING_MODE_ALL) {
  650. $metadataFiles[] = $file;
  651. }
  652. continue;
  653. }
  654. $id = $this->_fileNameToId($fileName);
  655. $metadatas = $this->_getMetadatas($id);
  656. if ($metadatas === FALSE) {
  657. $metadatas = array('expire' => 1, 'tags' => array());
  658. }
  659. switch ($mode) {
  660. case Zend_Cache::CLEANING_MODE_ALL:
  661. $result = $result && $this->remove($id);
  662. break;
  663. case Zend_Cache::CLEANING_MODE_OLD:
  664. if (time() > $metadatas['expire']) {
  665. $result = $this->remove($id) && $result;
  666. }
  667. break;
  668. case Zend_Cache::CLEANING_MODE_MATCHING_TAG:
  669. $matching = true;
  670. foreach ($tags as $tag) {
  671. if (!in_array($tag, $metadatas['tags'])) {
  672. $matching = false;
  673. break;
  674. }
  675. }
  676. if ($matching) {
  677. $result = $this->remove($id) && $result;
  678. }
  679. break;
  680. case Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG:
  681. $matching = false;
  682. foreach ($tags as $tag) {
  683. if (in_array($tag, $metadatas['tags'])) {
  684. $matching = true;
  685. break;
  686. }
  687. }
  688. if (!$matching) {
  689. $result = $this->remove($id) && $result;
  690. }
  691. break;
  692. case Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG:
  693. $matching = false;
  694. foreach ($tags as $tag) {
  695. if (in_array($tag, $metadatas['tags'])) {
  696. $matching = true;
  697. break;
  698. }
  699. }
  700. if ($matching) {
  701. $result = $this->remove($id) && $result;
  702. }
  703. break;
  704. default:
  705. Zend_Cache::throwException('Invalid mode for clean() method');
  706. break;
  707. }
  708. }
  709. if ((is_dir($file)) and ($this->_options['hashed_directory_level']>0)) {
  710. // Recursive call
  711. $result = $this->_clean($file . DIRECTORY_SEPARATOR, $mode, $tags) && $result;
  712. if ($mode == Zend_Cache::CLEANING_MODE_ALL) {
  713. // we try to drop the structure too
  714. @rmdir($file);
  715. }
  716. }
  717. }
  718. // cycle through metadataFiles and delete orphaned ones
  719. foreach ($metadataFiles as $file) {
  720. if (file_exists($file)) {
  721. $result = $this->_remove($file) && $result;
  722. }
  723. }
  724. return $result;
  725. }
  726. protected function _get($dir, $mode, $tags = array())
  727. {
  728. if (!is_dir($dir)) {
  729. return false;
  730. }
  731. $result = array();
  732. $prefix = $this->_options['file_name_prefix'];
  733. $glob = @glob($dir . $prefix . '--*');
  734. if ($glob === false) {
  735. // On some systems it is impossible to distinguish between empty match and an error.
  736. return array();
  737. }
  738. foreach ($glob as $file) {
  739. if (is_file($file)) {
  740. $fileName = basename($file);
  741. $id = $this->_fileNameToId($fileName);
  742. $metadatas = $this->_getMetadatas($id);
  743. if ($metadatas === FALSE) {
  744. continue;
  745. }
  746. if (time() > $metadatas['expire']) {
  747. continue;
  748. }
  749. switch ($mode) {
  750. case 'ids':
  751. $result[] = $id;
  752. break;
  753. case 'tags':
  754. $result = array_unique(array_merge($result, $metadatas['tags']));
  755. break;
  756. case 'matching':
  757. $matching = true;
  758. foreach ($tags as $tag) {
  759. if (!in_array($tag, $metadatas['tags'])) {
  760. $matching = false;
  761. break;
  762. }
  763. }
  764. if ($matching) {
  765. $result[] = $id;
  766. }
  767. break;
  768. case 'notMatching':
  769. $matching = false;
  770. foreach ($tags as $tag) {
  771. if (in_array($tag, $metadatas['tags'])) {
  772. $matching = true;
  773. break;
  774. }
  775. }
  776. if (!$matching) {
  777. $result[] = $id;
  778. }
  779. break;
  780. case 'matchingAny':
  781. $matching = false;
  782. foreach ($tags as $tag) {
  783. if (in_array($tag, $metadatas['tags'])) {
  784. $matching = true;
  785. break;
  786. }
  787. }
  788. if ($matching) {
  789. $result[] = $id;
  790. }
  791. break;
  792. default:
  793. Zend_Cache::throwException('Invalid mode for _get() method');
  794. break;
  795. }
  796. }
  797. if ((is_dir($file)) and ($this->_options['hashed_directory_level']>0)) {
  798. // Recursive call
  799. $recursiveRs = $this->_get($file . DIRECTORY_SEPARATOR, $mode, $tags);
  800. if ($recursiveRs === false) {
  801. $this->_log('Zend_Cache_Backend_File::_get() / recursive call : can\'t list entries of "'.$file.'"');
  802. } else {
  803. $result = array_unique(array_merge($result, $recursiveRs));
  804. }
  805. }
  806. }
  807. return array_unique($result);
  808. }
  809. /**
  810. * Compute & return the expire time
  811. *
  812. * @param int $lifetime
  813. * @return int expire time (unix timestamp)
  814. */
  815. protected function _expireTime($lifetime)
  816. {
  817. if ($lifetime === null) {
  818. return 9999999999;
  819. }
  820. return time() + $lifetime;
  821. }
  822. /**
  823. * Make a control key with the string containing datas
  824. *
  825. * @param string $data Data
  826. * @param string $controlType Type of control 'md5', 'crc32' or 'strlen'
  827. * @throws Zend_Cache_Exception
  828. * @return string Control key
  829. */
  830. protected function _hash($data, $controlType)
  831. {
  832. switch ($controlType) {
  833. case 'md5':
  834. return md5($data);
  835. case 'crc32':
  836. return crc32($data);
  837. case 'strlen':
  838. return strlen($data);
  839. case 'adler32':
  840. return hash('adler32', $data);
  841. default:
  842. Zend_Cache::throwException("Incorrect hash function : $controlType");
  843. }
  844. }
  845. /**
  846. * Transform a cache id into a file name and return it
  847. *
  848. * @param string $id Cache id
  849. * @return string File name
  850. */
  851. protected function _idToFileName($id)
  852. {
  853. $prefix = $this->_options['file_name_prefix'];
  854. $result = $prefix . '---' . $id;
  855. return $result;
  856. }
  857. /**
  858. * Make and return a file name (with path)
  859. *
  860. * @param string $id Cache id
  861. * @return string File name (with path)
  862. */
  863. protected function _file($id)
  864. {
  865. $path = $this->_path($id);
  866. $fileName = $this->_idToFileName($id);
  867. return $path . $fileName;
  868. }
  869. /**
  870. * Return the complete directory path of a filename (including hashedDirectoryStructure)
  871. *
  872. * @param string $id Cache id
  873. * @param boolean $parts if true, returns array of directory parts instead of single string
  874. * @return string Complete directory path
  875. */
  876. protected function _path($id, $parts = false)
  877. {
  878. $partsArray = array();
  879. $root = $this->_options['cache_dir'];
  880. $prefix = $this->_options['file_name_prefix'];
  881. if ($this->_options['hashed_directory_level']>0) {
  882. $hash = hash('adler32', $id);
  883. for ($i=0 ; $i < $this->_options['hashed_directory_level'] ; $i++) {
  884. $root = $root . $prefix . '--' . substr($hash, 0, $i + 1) . DIRECTORY_SEPARATOR;
  885. $partsArray[] = $root;
  886. }
  887. }
  888. if ($parts) {
  889. return $partsArray;
  890. } else {
  891. return $root;
  892. }
  893. }
  894. /**
  895. * Make the directory strucuture for the given id
  896. *
  897. * @param string $id cache id
  898. * @return boolean true
  899. */
  900. protected function _recursiveMkdirAndChmod($id)
  901. {
  902. if ($this->_options['hashed_directory_level'] <=0) {
  903. return true;
  904. }
  905. $partsArray = $this->_path($id, true);
  906. foreach ($partsArray as $part) {
  907. if (!is_dir($part)) {
  908. @mkdir($part, $this->_options['hashed_directory_perm']);
  909. @chmod($part, $this->_options['hashed_directory_perm']); // see #ZF-320 (this line is required in some configurations)
  910. }
  911. }
  912. return true;
  913. }
  914. /**
  915. * Test if the given cache id is available (and still valid as a cache record)
  916. *
  917. * @param string $id Cache id
  918. * @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested
  919. * @return boolean|mixed false (a cache is not available) or "last modified" timestamp (int) of the available cache record
  920. */
  921. protected function _test($id, $doNotTestCacheValidity)
  922. {
  923. $metadatas = $this->_getMetadatas($id);
  924. if (!$metadatas) {
  925. return false;
  926. }
  927. if ($doNotTestCacheValidity || (time() <= $metadatas['expire'])) {
  928. return $metadatas['mtime'];
  929. }
  930. return false;
  931. }
  932. /**
  933. * Return the file content of the given file
  934. *
  935. * @param string $file File complete path
  936. * @return string File content (or false if problem)
  937. */
  938. protected function _fileGetContents($file)
  939. {
  940. $result = false;
  941. if (!is_file($file)) {
  942. return false;
  943. }
  944. $f = @fopen($file, 'rb');
  945. if ($f) {
  946. if ($this->_options['file_locking']) @flock($f, LOCK_SH);
  947. $result = stream_get_contents($f);
  948. if ($this->_options['file_locking']) @flock($f, LOCK_UN);
  949. @fclose($f);
  950. }
  951. return $result;
  952. }
  953. /**
  954. * Put the given string into the given file
  955. *
  956. * @param string $file File complete path
  957. * @param string $string String to put in file
  958. * @return boolean true if no problem
  959. */
  960. protected function _filePutContents($file, $string)
  961. {
  962. $result = false;
  963. $f = @fopen($file, 'ab+');
  964. if ($f) {
  965. if ($this->_options['file_locking']) @flock($f, LOCK_EX);
  966. fseek($f, 0);
  967. ftruncate($f, 0);
  968. $tmp = @fwrite($f, $string);
  969. if (!($tmp === FALSE)) {
  970. $result = true;
  971. }
  972. @fclose($f);
  973. }
  974. @chmod($file, $this->_options['cache_file_perm']);
  975. return $result;
  976. }
  977. /**
  978. * Transform a file name into cache id and return it
  979. *
  980. * @param string $fileName File name
  981. * @return string Cache id
  982. */
  983. protected function _fileNameToId($fileName)
  984. {
  985. $prefix = $this->_options['file_name_prefix'];
  986. return preg_replace('~^' . $prefix . '---(.*)$~', '$1', $fileName);
  987. }
  988. }