1: <?php
2: class CacheFichier {
3: /**
4: * Options disponibles
5: *
6: * ====> (string) stockage_chemin :
7: * Chemin vers le dossier devant contenir l'arborescence du cache.
8: *
9: * =====> (boolean) fichier_verrou :
10: * - Active / Désactive le verrouillage des fichiers
11: * - Peut éviter la corruption du cache dans de mauvaises circonstances, mais cela ne fonctionne pas sur des serveur
12: * multithread et sur les systèmes de fichiers NFS par exemple.
13: *
14: * =====> (boolean) controle_lecture :
15: * - Activer / désactiver le contrôle de lecture
16: * - S'il est activé, une clé de contrôle est ajoutée dans le fichier de cache et cette clé est comparée avec celle calculée
17: * après la lecture.
18: *
19: * =====> (string) controle_lecture_type :
20: * Type de contrôle de lecture (seulement si le contrôle de lecture est activé).
21: * Les valeurs disponibles sont:
22: * - «md5» pour un contrôle md5 (le meilleur mais le plus lent)
23: * - «crc32» pour un contrôle de hachage crc32 (un peu moins sécurisé, mais plus rapide, un meilleur choix)
24: * - «adler32» pour un contrôle de hachage adler32 (excellent choix aussi, plus rapide que crc32)
25: * - «strlen» pour un test de longueur uniquement (le plus rapide)
26: *
27: * =====> (int) dossier_niveau :
28: * - Permet de réglez le nombre de niveau de sous-dossier que contiendra l'arborescence des dossiers du cache.
29: * 0 signifie "pas de sous-dossier pour le cache",
30: * 1 signifie "un niveau de sous-dossier",
31: * 2 signifie "deux niveaux" ...
32: * Cette option peut accélérer le cache seulement lorsque vous avez plusieurs centaines de fichiers de cache.
33: * Seuls des tests spécifiques peuvent vous aider à choisir la meilleure valeur possible pour vous.
34: * 1 ou 2 peut être est un bon début.
35: *
36: * =====> (int) dossier_umask :
37: * - Umask pour les sous-dossiers de l'arborescence du cache.
38: *
39: * =====> (string) fichier_prefixe :
40: * - préfixe pour les fichiers du cache
41: * - ATTENTION : faite vraiment attention avec cette option, car une valeur trop générique dans le dossier cache du système
42: * (comme /tmp) peut provoquer des catastrophes lors du nettoyage du cache.
43: *
44: * =====> (int) fichier_umask :
45: * - Umask pour les fichiers de cache
46: *
47: * =====> (int) metadonnees_max_taille :
48: * - taille maximum pour le tableau de métadonnées du cache (ne changer pas cette valeur sauf si vous savez ce que vous faite)
49: *
50: * @var array options disponibles
51: */
52: protected $options = array(
53: 'stockage_chemin' => null,
54: 'fichier_verrou' => true,
55: 'controle_lecture' => true,
56: 'controle_lecture_type' => 'crc32',
57: 'dossier_niveau' => 0,
58: 'dossier_umask' => 0700,
59: 'fichier_prefixe' => 'tbf',
60: 'fichier_umask' => 0600,
61: 'metadonnees_max_taille' => 100
62: );
63:
64: /**
65: * Array of metadatas (each item is an associative array)
66: *
67: * @var array
68: */
69: protected $metadonnees = array();
70:
71: private $Cache = null;
72:
73: /**
74: * Constructor
75: *
76: * @param array $options associative array of options
77: * @throws Zend_Cache_Exception
78: * @return void
79: */
80: public function __construct(array $options = array(), Cache $cache) {
81: $this->Cache = $cache;
82: $this->initialiserOptionsParConfig();
83: $this->setOptions($options);
84:
85: if (isset($this->options['prefixe_fichier'])) {
86: if (!preg_match('~^[a-zA-Z0-9_]+$~D', $this->options['prefixe_fichier'])) {
87: trigger_error("Préfixe de nom de fichier invalide : doit contenir seulement [a-zA-Z0-9_]", E_USER_WARNING);
88: }
89: }
90: if ($this->options['metadonnees_max_taille'] < 10) {
91: trigger_error("Taille du tableau des méta-données invalide, elle doit être > 10", E_USER_WARNING);
92: }
93: if (isset($options['dossier_umask']) && is_string($options['dossier_umask'])) {
94: // See #ZF-4422
95: $this->options['dossier_umask'] = octdec($this->options['dossier_umask']);
96: }
97: if (isset($options['fichier_umask']) && is_string($options['fichier_umask'])) {
98: // See #ZF-4422
99: $this->options['fichier_umask'] = octdec($this->options['fichier_umask']);
100: }
101: }
102:
103: private function initialiserOptionsParConfig() {
104: while (list($nom, $valeur) = each($this->options)) {
105: if (Config::existe($nom)) {
106: $this->options[$nom] = Config::get($nom);
107: }
108: }
109: }
110:
111: private function setOptions($options) {
112: while (list($nom, $valeur) = each($options)) {
113: if (!is_string($nom)) {
114: trigger_error("Nom d'option incorecte : $nom", E_USER_WARNING);
115: }
116: $nom = strtolower($nom);
117: if (array_key_exists($nom, $this->options)) {
118: $this->options[$nom] = $valeur;
119: }
120: }
121: }
122:
123: public function setEmplacement($emplacement) {
124: if (!is_dir($emplacement)) {
125: trigger_error("L'emplacement doit être un dossier.", E_USER_WARNING);
126: }
127: if (!is_writable($emplacement)) {
128: trigger_error("Le dossier de stockage du cache n'est pas accessible en écriture", E_USER_WARNING);
129: }
130: $emplacement = rtrim(realpath($emplacement), '\\/').DS;
131: $this->options['stockage_chemin'] = $emplacement;
132: }
133:
134: /**
135: * Test if a cache is available for the given id and (if yes) return it (false else)
136: *
137: * @param string $id cache id
138: * @param boolean $doNotTestCacheValidity if set to true, the cache validity won't be tested
139: * @return string|false cached datas
140: */
141: public function charger($id, $ne_pas_tester_validiter_du_cache = false) {
142: $donnees = false;
143: if ($this->tester($id, $ne_pas_tester_validiter_du_cache)) {
144: $metadonnees = $this->getMetadonneesFichier($id);
145: $fichier = $this->getFichierNom($id);
146: $donnees = $this->getContenuFichier($fichier);
147: if ($this->options['controle_lecture']) {
148: $cle_secu_donnees = $this->genererCleSecu($donnees, $this->options['controle_lecture_type']);
149: $cle_secu_controle = $metadonnees['hash'];
150: if ($cle_secu_donnees != $cle_secu_controle) {
151: // Probléme détecté par le contrôle de lecture !
152: // TODO : loguer le pb de sécu
153: $this->supprimer($id);
154: $donnees = false;
155: }
156: }
157: }
158: return $donnees;
159: }
160:
161: /**
162: * Teste si un enregistrement en cache est disponible ou pas (pour l'id passé en paramètre).
163: *
164: * @param string $id identifiant de cache.
165: * @return mixed false (le cache n'est pas disponible) ou timestamp (int) "de dernière modification" de l'enregistrement en cache
166: */
167: public function tester($id) {
168: clearstatcache();
169: return $this->testerExistenceCache($id, false);
170: }
171:
172: /**
173: * Save some string datas into a cache record
174: *
175: * Note : $data is always "string" (serialization is done by the
176: * core not by the backend)
177: *
178: * @param string $data Datas to cache
179: * @param string $id Cache id
180: * @param array $tags Array of strings, the cache record will be tagged by each string entry
181: * @param int $specificLifetime If != false, set a specific lifetime for this cache record (null => infinite lifetime)
182: * @return boolean true if no problem
183: */
184: public function sauver($donnees, $id, $tags = array(), $duree_vie_specifique = false) {
185: clearstatcache();
186: $fichier = $this->getFichierNom($id);
187: $chemin = $this->getChemin($id);
188:
189: $resultat = true;
190: if ($this->options['dossier_niveau'] > 0) {
191: if (!is_writable($chemin)) {
192: // maybe, we just have to build the directory structure
193: $this->lancerMkdirEtChmodRecursif($id);
194: }
195: if (!is_writable($chemin)) {
196: $resultat = false;
197: }
198: }
199:
200: if ($resultat === true) {
201: if ($this->options['controle_lecture']) {
202: $cle_secu = $this->genererCleSecu($donnees, $this->options['controle_lecture_type']);
203: } else {
204: $cle_secu = '';
205: }
206:
207: $metadonnees = array(
208: 'hash' => $cle_secu,
209: 'mtime' => time(),
210: 'expiration' => $this->Cache->getTimestampExpiration($duree_vie_specifique),
211: 'tags' => $tags
212: );
213:
214: if (! $resultat = $this->setMetadonnees($id, $metadonnees)) {
215: // TODO : ajouter un log
216: } else {
217: $resultat = $this->setContenuFichier($fichier, $donnees);
218: }
219: }
220: return $resultat;
221: }
222:
223: /**
224: * Remove a cache record
225: *
226: * @param string $id cache id
227: * @return boolean true if no problem
228: */
229: public function supprimer($id) {
230: $fichier = $this->getFichierNom($id);
231: $suppression_fichier = $this->supprimerFichier($fichier);
232: $suppression_metadonnees = $this->supprimerMetadonnees($id);
233: return $suppression_metadonnees && $suppression_fichier;
234: }
235:
236: /**
237: * Clean some cache records
238: *
239: * Available modes are :
240: * 'all' (default) => remove all cache entries ($tags is not used)
241: * 'old' => remove too old cache entries ($tags is not used)
242: * 'matchingTag' => remove cache entries matching all given tags
243: * ($tags can be an array of strings or a single string)
244: * 'notMatchingTag' => remove cache entries not matching one of the given tags
245: * ($tags can be an array of strings or a single string)
246: * 'matchingAnyTag' => remove cache entries matching any given tags
247: * ($tags can be an array of strings or a single string)
248: *
249: * @param string $mode clean mode
250: * @param tags array $tags array of tags
251: * @return boolean true if no problem
252: */
253: public function nettoyer($mode = Cache::NETTOYAGE_MODE_TOUS, $tags = array()) {
254: // We use this protected method to hide the recursive stuff
255: clearstatcache();
256: return $this->nettoyerFichiers($this->options['stockage_chemin'], $mode, $tags);
257: }
258:
259: /**
260: * Return an array of stored cache ids
261: *
262: * @return array array of stored cache ids (string)
263: */
264: public function getIds() {
265: return $this->analyserCache($this->options['stockage_chemin'], 'ids', array());
266: }
267:
268: /**
269: * Return an array of stored tags
270: *
271: * @return array array of stored tags (string)
272: */
273: public function getTags() {
274: return $this->analyserCache($this->options['stockage_chemin'], 'tags', array());
275: }
276:
277: /**
278: * Return an array of stored cache ids which match given tags
279: *
280: * In case of multiple tags, a logical AND is made between tags
281: *
282: * @param array $tags array of tags
283: * @return array array of matching cache ids (string)
284: */
285: public function getIdsAvecLesTags($tags = array()) {
286: return $this->analyserCache($this->options['stockage_chemin'], 'matching', $tags);
287: }
288:
289: /**
290: * Return an array of stored cache ids which don't match given tags
291: *
292: * In case of multiple tags, a logical OR is made between tags
293: *
294: * @param array $tags array of tags
295: * @return array array of not matching cache ids (string)
296: */
297: public function getIdsSansLesTags($tags = array()) {
298: return $this->analyserCache($this->options['stockage_chemin'], 'notMatching', $tags);
299: }
300:
301: /**
302: * Return an array of stored cache ids which match any given tags
303: *
304: * In case of multiple tags, a logical AND is made between tags
305: *
306: * @param array $tags array of tags
307: * @return array array of any matching cache ids (string)
308: */
309: public function getIdsAvecUnTag($tags = array()) {
310: return $this->analyserCache($this->options['stockage_chemin'], 'matchingAny', $tags);
311: }
312:
313: /**
314: * Return the filling percentage of the backend storage
315: *
316: * @throws Zend_Cache_Exception
317: * @return int integer between 0 and 100
318: */
319: public function getPourcentageRemplissage() {
320: $libre = disk_free_space($this->options['stockage_chemin']);
321: $total = disk_total_space($this->options['stockage_chemin']);
322:
323: $pourcentage = 0;
324: if ($total == 0) {
325: trigger_error("Impossible d'utiliser la fonction disk_total_space", E_USER_WARNING);
326: } else {
327: $pourcentage = ($libre >= $total) ? 100 : ((int) (100. * ($total - $libre) / $total));
328: }
329: return $pourcentage;
330: }
331:
332: /**
333: * Return an array of metadatas for the given cache id
334: *
335: * The array must include these keys :
336: * - expire : the expire timestamp
337: * - tags : a string array of tags
338: * - mtime : timestamp of last modification time
339: *
340: * @param string $id cache id
341: * @return array array of metadatas (false if the cache id is not found)
342: */
343: public function getMetadonnees($id) {
344: if ($metadonnees = $this->getMetadonneesFichier($id)) {
345: if (time() > $metadonnees['expiration']) {
346: $metadonnees = false;
347: } else {
348: $metadonnees = array(
349: 'expiration' => $metadonnees['expiration'],
350: 'tags' => $metadonnees['tags'],
351: 'mtime' => $metadonnees['mtime']
352: );
353: }
354: }
355:
356: return $metadonnees;
357: }
358:
359: /**
360: * Give (if possible) an extra lifetime to the given cache id
361: *
362: * @param string $id cache id
363: * @param int $extraLifetime
364: * @return boolean true if ok
365: */
366: public function ajouterSupplementDureeDeVie($id, $supplement_duree_de_vie) {
367: $augmentation = true;
368: if ($metadonnees = $this->getMetadonneesFichier($id)) {
369: if (time() > $metadonnees['expiration']) {
370: $augmentation = false;
371: } else {
372: $metadonnees_nouvelle = array(
373: 'hash' => $metadonnees['hash'],
374: 'mtime' => time(),
375: 'expiration' => $metadonnees['expiration'] + $supplement_duree_de_vie,
376: 'tags' => $metadonnees['tags']
377: );
378: $augmentation = $this->setMetadonnees($id, $metadonnees_nouvelle);
379: }
380: }
381: return $augmentation;
382: }
383:
384: /**
385: * Get a metadatas record
386: *
387: * @param string $id Cache id
388: * @return array|false Associative array of metadatas
389: */
390: protected function getMetadonneesFichier($id) {
391: $metadonnees = false;
392: if (isset($this->metadonnees[$id])) {
393: $metadonnees = $this->metadonnees[$id];
394: } else {
395: if ($metadonnees = $this->chargerMetadonnees($id)) {
396: $this->setMetadonnees($id, $metadonnees, false);
397: }
398: }
399: return $metadonnees;
400: }
401:
402: /**
403: * Set a metadatas record
404: *
405: * @param string $id Cache id
406: * @param array $metadatas Associative array of metadatas
407: * @param boolean $save optional pass false to disable saving to file
408: * @return boolean True if no problem
409: */
410: protected function setMetadonnees($id, $metadonnees, $sauvegarde = true) {
411: if (count($this->metadonnees) >= $this->options['metadonnees_max_taille']) {
412: $n = (int) ($this->options['metadonnees_max_taille'] / 10);
413: $this->metadonnees = array_slice($this->metadonnees, $n);
414: }
415:
416: $resultat = true;
417: if ($sauvegarde) {
418: $resultat = $this->sauverMetadonnees($id, $metadonnees);
419: }
420: if ($resultat == true) {
421: $this->metadonnees[$id] = $metadonnees;
422: }
423: return $resultat;
424: }
425:
426: /**
427: * Drop a metadata record
428: *
429: * @param string $id Cache id
430: * @return boolean True if no problem
431: */
432: protected function supprimerMetadonnees($id) {
433: if (isset($this->metadonnees[$id])) {
434: unset($this->metadonnees[$id]);
435: }
436: $fichier_meta = $this->getNomFichierMeta($id);
437: return $this->supprimerFichier($fichier_meta);
438: }
439:
440: /**
441: * Clear the metadatas array
442: *
443: * @return void
444: */
445: protected function nettoyerMetadonnees() {
446: $this->metadonnees = array();
447: }
448:
449: /**
450: * Load metadatas from disk
451: *
452: * @param string $id Cache id
453: * @return array|false Metadatas associative array
454: */
455: protected function chargerMetadonnees($id) {
456: $fichier = $this->getNomFichierMeta($id);
457: if ($resultat = $this->getContenuFichier($fichier)) {
458: $resultat = @unserialize($resultat);
459: }
460: return $resultat;
461: }
462:
463: /**
464: * Save metadatas to disk
465: *
466: * @param string $id Cache id
467: * @param array $metadatas Associative array
468: * @return boolean True if no problem
469: */
470: protected function sauverMetadonnees($id, $metadonnees) {
471: $fichier = $this->getNomFichierMeta($id);
472: $resultat = $this->setContenuFichier($fichier, serialize($metadonnees));
473: return $resultat;
474: }
475:
476: /**
477: * Make and return a file name (with path) for metadatas
478: *
479: * @param string $id Cache id
480: * @return string Metadatas file name (with path)
481: */
482: protected function getNomFichierMeta($id) {
483: $chemin = $this->getChemin($id);
484: $fichier_nom = $this->transformaterIdEnNomFichier('interne-meta---'.$id);
485: return $chemin.$fichier_nom;
486: }
487:
488: /**
489: * Check if the given filename is a metadatas one
490: *
491: * @param string $fileName File name
492: * @return boolean True if it's a metadatas one
493: */
494: protected function etreFichierMeta($fichier_nom) {
495: $id = $this->transformerNomFichierEnId($fichier_nom);
496: return (substr($id, 0, 21) == 'interne-meta---') ? true : false;
497: }
498:
499: /**
500: * Remove a file
501: *
502: * If we can't remove the file (because of locks or any problem), we will touch
503: * the file to invalidate it
504: *
505: * @param string $file Complete file path
506: * @return boolean True if ok
507: */
508: protected function supprimerFichier($fichier) {
509: $resultat = false;
510: if (is_file($fichier)) {
511: if ($resultat = @unlink($fichier)) {
512: // TODO : ajouter un log
513: }
514: }
515: return $resultat;
516: }
517:
518: /**
519: * Clean some cache records (protected method used for recursive stuff)
520: *
521: * Available modes are :
522: * Zend_Cache::CLEANING_MODE_ALL (default) => remove all cache entries ($tags is not used)
523: * Zend_Cache::CLEANING_MODE_OLD => remove too old cache entries ($tags is not used)
524: * Zend_Cache::CLEANING_MODE_MATCHING_TAG => remove cache entries matching all given tags
525: * ($tags can be an array of strings or a single string)
526: * Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG => remove cache entries not {matching one of the given tags}
527: * ($tags can be an array of strings or a single string)
528: * Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG => remove cache entries matching any given tags
529: * ($tags can be an array of strings or a single string)
530: *
531: * @param string $dir Directory to clean
532: * @param string $mode Clean mode
533: * @param array $tags Array of tags
534: * @throws Zend_Cache_Exception
535: * @return boolean True if no problem
536: */
537: protected function nettoyerFichiers($dossier, $mode = Cache::NETTOYAGE_MODE_TOUS, $tags = array()) {
538: if (!is_dir($dossier)) {
539: return false;
540: }
541: $resultat = true;
542: $prefixe = $this->options['fichier_prefixe'];
543: $glob = @glob($dossier.$prefixe.'--*');
544: if ($glob === false) {
545: // On some systems it is impossible to distinguish between empty match and an error.
546: return true;
547: }
548: foreach ($glob as $fichier) {
549: if (is_file($fichier)) {
550: $fichier_nom = basename($fichier);
551: if ($this->etreFichierMeta($fichier_nom)) {
552: // Pour le mode Cache::NETTOYAGE_MODE_TOUS, nous essayons de tous supprimer même les vieux fichiers méta
553: if ($mode != Cache::NETTOYAGE_MODE_TOUS) {
554: continue;
555: }
556: }
557: $id = $this->transformerNomFichierEnId($fichier_nom);
558: $metadonnees = $this->getMetadonneesFichier($id);
559: if ($metadonnees === FALSE) {
560: $metadonnees = array('expiration' => 1, 'tags' => array());
561: }
562: switch ($mode) {
563: case Cache::NETTOYAGE_MODE_TOUS :
564: if ($resultat_suppression = $this->supprimer($id)) {
565: // Dans ce cas seulement, nous acception qu'il y ait un problème avec la suppresssion du fichier meta
566: $resultat_suppression = $this->supprimerFichier($fichier);
567: }
568: $resultat = $resultat && $resultat_suppression;
569: break;
570: case Cache::NETTOYAGE_MODE_EXPIRATION :
571: if (time() > $metadonnees['expiration']) {
572: $resultat = $this->supprimer($id) && $resultat;
573: }
574: break;
575: case Cache::NETTOYAGE_MODE_AVEC_LES_TAGS :
576: $correspondance = true;
577: foreach ($tags as $tag) {
578: if (!in_array($tag, $metadonnees['tags'])) {
579: $correspondance = false;
580: break;
581: }
582: }
583: if ($correspondance) {
584: $resultat = $this->supprimer($id) && $resultat;
585: }
586: break;
587: case Cache::NETTOYAGE_MODE_SANS_LES_TAGS :
588: $correspondance = false;
589: foreach ($tags as $tag) {
590: if (in_array($tag, $metadonnees['tags'])) {
591: $correspondance = true;
592: break;
593: }
594: }
595: if (!$correspondance) {
596: $resultat = $this->supprimer($id) && $resultat;
597: }
598: break;
599: case Cache::NETTOYAGE_MODE_AVEC_UN_TAG :
600: $correspondance = false;
601: foreach ($tags as $tag) {
602: if (in_array($tag, $metadonnees['tags'])) {
603: $correspondance = true;
604: break;
605: }
606: }
607: if ($correspondance) {
608: $resultat = $this->supprimer($id) && $resultat;
609: }
610: break;
611: default:
612: trigger_error("Mode de nettoyage invalide pour la méthode nettoyer()", E_USER_WARNING);
613: break;
614: }
615: }
616: if ((is_dir($fichier)) and ($this->options['dossier_niveau'] > 0)) {
617: // Appel récursif
618: $resultat = $this->nettoyerFichiers($fichier.DS, $mode, $tags) && $resultat;
619: if ($mode == Cache::NETTOYAGE_MODE_TOUS) {
620: // Si mode == Cache::NETTOYAGE_MODE_TOUS, nous essayons de supprimer la structure aussi
621: @rmdir($fichier);
622: }
623: }
624: }
625: return $resultat;
626: }
627:
628: protected function analyserCache($dossier, $mode, $tags = array()) {
629: if (!is_dir($dossier)) {
630: return false;
631: }
632: $resultat = array();
633: $prefixe = $this->options['fichier_prefixe'];
634: $glob = @glob($dossier.$prefixe.'--*');
635: if ($glob === false) {
636: // On some systems it is impossible to distinguish between empty match and an error.
637: return array();
638: }
639: foreach ($glob as $fichier) {
640: if (is_file($fichier)) {
641: $nom_fichier = basename($fichier);
642: $id = $this->transformerNomFichierEnId($nom_fichier);
643: $metadonnees = $this->getMetadonneesFichier($id);
644: if ($metadonnees === FALSE) {
645: continue;
646: }
647: if (time() > $metadonnees['expiration']) {
648: continue;
649: }
650: switch ($mode) {
651: case 'ids':
652: $resultat[] = $id;
653: break;
654: case 'tags':
655: $resultat = array_unique(array_merge($resultat, $metadonnees['tags']));
656: break;
657: case 'matching':
658: $correspondance = true;
659: foreach ($tags as $tag) {
660: if (!in_array($tag, $metadonnees['tags'])) {
661: $correspondance = false;
662: break;
663: }
664: }
665: if ($correspondance) {
666: $resultat[] = $id;
667: }
668: break;
669: case 'notMatching':
670: $correspondance = false;
671: foreach ($tags as $tag) {
672: if (in_array($tag, $metadonnees['tags'])) {
673: $correspondance = true;
674: break;
675: }
676: }
677: if (!$correspondance) {
678: $resultat[] = $id;
679: }
680: break;
681: case 'matchingAny':
682: $correspondance = false;
683: foreach ($tags as $tag) {
684: if (in_array($tag, $metadonnees['tags'])) {
685: $correspondance = true;
686: break;
687: }
688: }
689: if ($correspondance) {
690: $resultat[] = $id;
691: }
692: break;
693: default:
694: trigger_error("Mode invalide pour la méthode analyserCache()", E_USER_WARNING);
695: break;
696: }
697: }
698: if ((is_dir($fichier)) and ($this->options['dossier_niveau'] > 0)) {
699: // Appel récursif
700: $resultat_analyse_recursive = $this->analyserCache($fichier.DS, $mode, $tags);
701: if ($resultat_analyse_recursive === false) {
702: // TODO : ajoute un log
703: } else {
704: $resultat = array_unique(array_merge($resultat, $resultat_analyse_recursive));
705: }
706: }
707: }
708: return array_unique($resultat);
709: }
710:
711: /**
712: * Make a control key with the string containing datas
713: *
714: * @param string $data Data
715: * @param string $controlType Type of control 'md5', 'crc32' or 'strlen'
716: * @throws Zend_Cache_Exception
717: * @return string Control key
718: */
719: protected function genererCleSecu($donnees, $type_de_controle) {
720: switch ($type_de_controle) {
721: case 'md5':
722: return md5($donnees);
723: case 'crc32':
724: return crc32($donnees);
725: case 'strlen':
726: return strlen($donnees);
727: case 'adler32':
728: return hash('adler32', $donnees);
729: default:
730: trigger_error("Fonction de génération de clé de sécurité introuvable : $type_de_controle", E_USER_WARNING);
731: }
732: }
733:
734: /**
735: * Transform a cache id into a file name and return it
736: *
737: * @param string $id Cache id
738: * @return string File name
739: */
740: protected function transformaterIdEnNomFichier($id) {
741: $prefixe = $this->options['fichier_prefixe'];
742: $resultat = $prefixe.'---'.$id;
743: return $resultat;
744: }
745:
746: /**
747: * Make and return a file name (with path)
748: *
749: * @param string $id Cache id
750: * @return string File name (with path)
751: */
752: protected function getFichierNom($id) {
753: $path = $this->getChemin($id);
754: $fileName = $this->transformaterIdEnNomFichier($id);
755: return $path . $fileName;
756: }
757:
758: /**
759: * Return the complete directory path of a filename (including hashedDirectoryStructure)
760: *
761: * @param string $id Cache id
762: * @param boolean $decoupage if true, returns array of directory parts instead of single string
763: * @return string Complete directory path
764: */
765: protected function getChemin($id, $decoupage = false) {
766: $morceaux = array();
767: $chemin = $this->options['stockage_chemin'];
768: $prefixe = $this->options['fichier_prefixe'];
769: if ($this->options['dossier_niveau'] > 0) {
770: $hash = hash('adler32', $id);
771: for ($i = 0 ; $i < $this->options['dossier_niveau'] ; $i++) {
772: $chemin .= $prefixe.'--'.substr($hash, 0, $i + 1).DS;
773: $morceaux[] = $chemin;
774: }
775: }
776: return ($decoupage) ? $morceaux : $chemin;
777: }
778:
779: /**
780: * Make the directory strucuture for the given id
781: *
782: * @param string $id cache id
783: * @return boolean true
784: */
785: protected function lancerMkdirEtChmodRecursif($id) {
786: $resultat = true;
787: if ($this->options['dossier_niveau'] > 0) {
788: $chemins = $this->getChemin($id, true);
789: foreach ($chemins as $chemin) {
790: if (!is_dir($chemin)) {
791: @mkdir($chemin, $this->options['dossier_umask']);
792: @chmod($chemin, $this->options['dossier_umask']); // see #ZF-320 (this line is required in some configurations)
793: }
794: }
795: }
796: return $resultat;
797: }
798:
799: /**
800: * Test if the given cache id is available (and still valid as a cache record)
801: *
802: * @param string $id Cache id
803: * @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested
804: * @return boolean|mixed false (a cache is not available) or "last modified" timestamp (int) of the available cache record
805: */
806: protected function testerExistenceCache($id, $ne_pas_tester_validiter_du_cache) {
807: $resultat = false;
808: if ($metadonnees = $this->getMetadonnees($id)) {
809: if ($ne_pas_tester_validiter_du_cache || (time() <= $metadonnees['expiration'])) {
810: $resultat = $metadonnees['mtime'];
811: }
812: }
813: return $resultat;
814: }
815:
816: /**
817: * Return the file content of the given file
818: *
819: * @param string $file File complete path
820: * @return string File content (or false if problem)
821: */
822: protected function getContenuFichier($fichier) {
823: $resultat = false;
824: if (is_file($fichier)) {
825: $f = @fopen($fichier, 'rb');
826: if ($f) {
827: if ($this->options['fichier_verrou']) @flock($f, LOCK_SH);
828: $resultat = stream_get_contents($f);
829: if ($this->options['fichier_verrou']) @flock($f, LOCK_UN);
830: @fclose($f);
831: }
832: }
833: return $resultat;
834: }
835:
836: /**
837: * Put the given string into the given file
838: *
839: * @param string $file File complete path
840: * @param string $string String to put in file
841: * @return boolean true if no problem
842: */
843: protected function setContenuFichier($fichier, $chaine) {
844: $resultat = false;
845: $f = @fopen($fichier, 'ab+');
846: if ($f) {
847: if ($this->options['fichier_verrou']) @flock($f, LOCK_EX);
848: fseek($f, 0);
849: ftruncate($f, 0);
850: $tmp = @fwrite($f, $chaine);
851: if (!($tmp === FALSE)) {
852: $resultat = true;
853: }
854: @fclose($f);
855: }
856: @chmod($fichier, $this->options['fichier_umask']);
857: return $resultat;
858: }
859:
860: /**
861: * Transform a file name into cache id and return it
862: *
863: * @param string $fileName File name
864: * @return string Cache id
865: */
866: protected function transformerNomFichierEnId($nom_de_fichier) {
867: $prefixe = $this->options['fichier_prefixe'];
868: return preg_replace('~^' . $prefixe . '---(.*)$~', '$1', $nom_de_fichier);
869: }
870: }
871: ?>