PageRenderTime 63ms CodeModel.GetById 11ms RepoModel.GetById 1ms app.codeStats 0ms

/library/Zend/Cache/Backend/File.php

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