246 |
jpm |
1 |
<?php
|
|
|
2 |
class CacheFichier {
|
|
|
3 |
/**
|
282 |
jpm |
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.
|
246 |
jpm |
13 |
*
|
282 |
jpm |
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.
|
246 |
jpm |
18 |
*
|
282 |
jpm |
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)
|
246 |
jpm |
26 |
*
|
282 |
jpm |
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.
|
246 |
jpm |
35 |
*
|
282 |
jpm |
36 |
* =====> (int) dossier_umask :
|
|
|
37 |
* - Umask pour les sous-dossiers de l'arborescence du cache.
|
246 |
jpm |
38 |
*
|
282 |
jpm |
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.
|
246 |
jpm |
43 |
*
|
282 |
jpm |
44 |
* =====> (int) fichier_umask :
|
|
|
45 |
* - Umask pour les fichiers de cache
|
246 |
jpm |
46 |
*
|
282 |
jpm |
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)
|
246 |
jpm |
49 |
*
|
282 |
jpm |
50 |
* @var array options disponibles
|
246 |
jpm |
51 |
*/
|
|
|
52 |
protected $options = array(
|
|
|
53 |
'stockage_chemin' => null,
|
248 |
jpm |
54 |
'fichier_verrou' => true,
|
|
|
55 |
'controle_lecture' => true,
|
246 |
jpm |
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 |
*/
|
248 |
jpm |
69 |
protected $metadonnees = array();
|
246 |
jpm |
70 |
|
269 |
jpm |
71 |
private $Cache = null;
|
|
|
72 |
|
246 |
jpm |
73 |
/**
|
|
|
74 |
* Constructor
|
|
|
75 |
*
|
|
|
76 |
* @param array $options associative array of options
|
|
|
77 |
* @throws Zend_Cache_Exception
|
|
|
78 |
* @return void
|
|
|
79 |
*/
|
269 |
jpm |
80 |
public function __construct(array $options = array(), Cache $cache) {
|
|
|
81 |
$this->Cache = $cache;
|
282 |
jpm |
82 |
$this->initialiserOptionsParConfig();
|
248 |
jpm |
83 |
$this->setOptions($options);
|
|
|
84 |
|
246 |
jpm |
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 |
}
|
248 |
jpm |
90 |
if ($this->options['metadonnees_max_taille'] < 10) {
|
246 |
jpm |
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 |
}
|
248 |
jpm |
102 |
|
282 |
jpm |
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 |
|
248 |
jpm |
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 |
}
|
246 |
jpm |
122 |
|
248 |
jpm |
123 |
public function setEmplacement($emplacement) {
|
246 |
jpm |
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 |
*/
|
248 |
jpm |
184 |
public function sauver($donnees, $id, $tags = array(), $duree_vie_specifique = false) {
|
246 |
jpm |
185 |
clearstatcache();
|
248 |
jpm |
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)) {
|
246 |
jpm |
192 |
// maybe, we just have to build the directory structure
|
|
|
193 |
$this->lancerMkdirEtChmodRecursif($id);
|
|
|
194 |
}
|
248 |
jpm |
195 |
if (!is_writable($chemin)) {
|
|
|
196 |
$resultat = false;
|
246 |
jpm |
197 |
}
|
|
|
198 |
}
|
248 |
jpm |
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(),
|
269 |
jpm |
210 |
'expiration' => $this->Cache->getTimestampExpiration($duree_vie_specifique),
|
248 |
jpm |
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 |
}
|
246 |
jpm |
219 |
}
|
248 |
jpm |
220 |
return $resultat;
|
246 |
jpm |
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();
|
248 |
jpm |
256 |
return $this->nettoyerFichiers($this->options['stockage_chemin'], $mode, $tags);
|
246 |
jpm |
257 |
}
|
|
|
258 |
|
|
|
259 |
/**
|
|
|
260 |
* Return an array of stored cache ids
|
|
|
261 |
*
|
|
|
262 |
* @return array array of stored cache ids (string)
|
|
|
263 |
*/
|
248 |
jpm |
264 |
public function getIds() {
|
|
|
265 |
return $this->analyserCache($this->options['stockage_chemin'], 'ids', array());
|
246 |
jpm |
266 |
}
|
|
|
267 |
|
|
|
268 |
/**
|
|
|
269 |
* Return an array of stored tags
|
|
|
270 |
*
|
|
|
271 |
* @return array array of stored tags (string)
|
|
|
272 |
*/
|
248 |
jpm |
273 |
public function getTags() {
|
|
|
274 |
return $this->analyserCache($this->options['stockage_chemin'], 'tags', array());
|
246 |
jpm |
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 |
*/
|
248 |
jpm |
285 |
public function getIdsAvecLesTags($tags = array()) {
|
|
|
286 |
return $this->analyserCache($this->options['stockage_chemin'], 'matching', $tags);
|
246 |
jpm |
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 |
*/
|
248 |
jpm |
297 |
public function getIdsSansLesTags($tags = array()) {
|
|
|
298 |
return $this->analyserCache($this->options['stockage_chemin'], 'notMatching', $tags);
|
246 |
jpm |
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 |
*/
|
248 |
jpm |
309 |
public function getIdsAvecUnTag($tags = array()) {
|
|
|
310 |
return $this->analyserCache($this->options['stockage_chemin'], 'matchingAny', $tags);
|
246 |
jpm |
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 |
*/
|
248 |
jpm |
319 |
public function getPourcentageRemplissage() {
|
250 |
jpm |
320 |
$libre = disk_free_space($this->options['stockage_chemin']);
|
|
|
321 |
$total = disk_total_space($this->options['stockage_chemin']);
|
248 |
jpm |
322 |
|
|
|
323 |
$pourcentage = 0;
|
246 |
jpm |
324 |
if ($total == 0) {
|
248 |
jpm |
325 |
trigger_error("Impossible d'utiliser la fonction disk_total_space", E_USER_WARNING);
|
246 |
jpm |
326 |
} else {
|
248 |
jpm |
327 |
$pourcentage = ($libre >= $total) ? 100 : ((int) (100. * ($total - $libre) / $total));
|
246 |
jpm |
328 |
}
|
248 |
jpm |
329 |
return $pourcentage;
|
246 |
jpm |
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 |
*/
|
248 |
jpm |
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 |
}
|
246 |
jpm |
354 |
}
|
248 |
jpm |
355 |
|
|
|
356 |
return $metadonnees;
|
246 |
jpm |
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 |
*/
|
248 |
jpm |
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 |
}
|
246 |
jpm |
380 |
}
|
248 |
jpm |
381 |
return $augmentation;
|
246 |
jpm |
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);
|
253 |
jpm |
484 |
$fichier_nom = $this->transformaterIdEnNomFichier('interne-meta---'.$id);
|
246 |
jpm |
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);
|
253 |
jpm |
496 |
return (substr($id, 0, 21) == 'interne-meta---') ? true : false;
|
246 |
jpm |
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)) {
|
248 |
jpm |
552 |
// Pour le mode Cache::NETTOYAGE_MODE_TOUS, nous essayons de tous supprimer même les vieux fichiers méta
|
246 |
jpm |
553 |
if ($mode != Cache::NETTOYAGE_MODE_TOUS) {
|
|
|
554 |
continue;
|
|
|
555 |
}
|
|
|
556 |
}
|
|
|
557 |
$id = $this->transformerNomFichierEnId($fichier_nom);
|
248 |
jpm |
558 |
$metadonnees = $this->getMetadonneesFichier($id);
|
|
|
559 |
if ($metadonnees === FALSE) {
|
|
|
560 |
$metadonnees = array('expiration' => 1, 'tags' => array());
|
246 |
jpm |
561 |
}
|
|
|
562 |
switch ($mode) {
|
248 |
jpm |
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);
|
246 |
jpm |
567 |
}
|
248 |
jpm |
568 |
$resultat = $resultat && $resultat_suppression;
|
246 |
jpm |
569 |
break;
|
248 |
jpm |
570 |
case Cache::NETTOYAGE_MODE_EXPIRATION :
|
|
|
571 |
if (time() > $metadonnees['expiration']) {
|
|
|
572 |
$resultat = $this->supprimer($id) && $resultat;
|
246 |
jpm |
573 |
}
|
|
|
574 |
break;
|
248 |
jpm |
575 |
case Cache::NETTOYAGE_MODE_AVEC_LES_TAGS :
|
|
|
576 |
$correspondance = true;
|
246 |
jpm |
577 |
foreach ($tags as $tag) {
|
248 |
jpm |
578 |
if (!in_array($tag, $metadonnees['tags'])) {
|
|
|
579 |
$correspondance = false;
|
246 |
jpm |
580 |
break;
|
|
|
581 |
}
|
|
|
582 |
}
|
248 |
jpm |
583 |
if ($correspondance) {
|
|
|
584 |
$resultat = $this->supprimer($id) && $resultat;
|
246 |
jpm |
585 |
}
|
|
|
586 |
break;
|
248 |
jpm |
587 |
case Cache::NETTOYAGE_MODE_SANS_LES_TAGS :
|
|
|
588 |
$correspondance = false;
|
246 |
jpm |
589 |
foreach ($tags as $tag) {
|
248 |
jpm |
590 |
if (in_array($tag, $metadonnees['tags'])) {
|
|
|
591 |
$correspondance = true;
|
246 |
jpm |
592 |
break;
|
|
|
593 |
}
|
|
|
594 |
}
|
248 |
jpm |
595 |
if (!$correspondance) {
|
|
|
596 |
$resultat = $this->supprimer($id) && $resultat;
|
246 |
jpm |
597 |
}
|
|
|
598 |
break;
|
248 |
jpm |
599 |
case Cache::NETTOYAGE_MODE_AVEC_UN_TAG :
|
|
|
600 |
$correspondance = false;
|
246 |
jpm |
601 |
foreach ($tags as $tag) {
|
248 |
jpm |
602 |
if (in_array($tag, $metadonnees['tags'])) {
|
|
|
603 |
$correspondance = true;
|
246 |
jpm |
604 |
break;
|
|
|
605 |
}
|
|
|
606 |
}
|
248 |
jpm |
607 |
if ($correspondance) {
|
|
|
608 |
$resultat = $this->supprimer($id) && $resultat;
|
246 |
jpm |
609 |
}
|
|
|
610 |
break;
|
|
|
611 |
default:
|
248 |
jpm |
612 |
trigger_error("Mode de nettoyage invalide pour la méthode nettoyer()", E_USER_WARNING);
|
246 |
jpm |
613 |
break;
|
|
|
614 |
}
|
|
|
615 |
}
|
248 |
jpm |
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);
|
246 |
jpm |
622 |
}
|
|
|
623 |
}
|
|
|
624 |
}
|
|
|
625 |
return $resultat;
|
|
|
626 |
}
|
|
|
627 |
|
248 |
jpm |
628 |
protected function analyserCache($dossier, $mode, $tags = array()) {
|
|
|
629 |
if (!is_dir($dossier)) {
|
246 |
jpm |
630 |
return false;
|
|
|
631 |
}
|
248 |
jpm |
632 |
$resultat = array();
|
|
|
633 |
$prefixe = $this->options['fichier_prefixe'];
|
|
|
634 |
$glob = @glob($dossier.$prefixe.'--*');
|
246 |
jpm |
635 |
if ($glob === false) {
|
|
|
636 |
// On some systems it is impossible to distinguish between empty match and an error.
|
|
|
637 |
return array();
|
|
|
638 |
}
|
248 |
jpm |
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) {
|
246 |
jpm |
645 |
continue;
|
|
|
646 |
}
|
248 |
jpm |
647 |
if (time() > $metadonnees['expiration']) {
|
246 |
jpm |
648 |
continue;
|
|
|
649 |
}
|
|
|
650 |
switch ($mode) {
|
|
|
651 |
case 'ids':
|
248 |
jpm |
652 |
$resultat[] = $id;
|
246 |
jpm |
653 |
break;
|
|
|
654 |
case 'tags':
|
248 |
jpm |
655 |
$resultat = array_unique(array_merge($resultat, $metadonnees['tags']));
|
246 |
jpm |
656 |
break;
|
|
|
657 |
case 'matching':
|
248 |
jpm |
658 |
$correspondance = true;
|
246 |
jpm |
659 |
foreach ($tags as $tag) {
|
248 |
jpm |
660 |
if (!in_array($tag, $metadonnees['tags'])) {
|
|
|
661 |
$correspondance = false;
|
246 |
jpm |
662 |
break;
|
|
|
663 |
}
|
|
|
664 |
}
|
248 |
jpm |
665 |
if ($correspondance) {
|
|
|
666 |
$resultat[] = $id;
|
246 |
jpm |
667 |
}
|
|
|
668 |
break;
|
|
|
669 |
case 'notMatching':
|
248 |
jpm |
670 |
$correspondance = false;
|
246 |
jpm |
671 |
foreach ($tags as $tag) {
|
248 |
jpm |
672 |
if (in_array($tag, $metadonnees['tags'])) {
|
|
|
673 |
$correspondance = true;
|
246 |
jpm |
674 |
break;
|
|
|
675 |
}
|
|
|
676 |
}
|
248 |
jpm |
677 |
if (!$correspondance) {
|
|
|
678 |
$resultat[] = $id;
|
246 |
jpm |
679 |
}
|
|
|
680 |
break;
|
|
|
681 |
case 'matchingAny':
|
248 |
jpm |
682 |
$correspondance = false;
|
246 |
jpm |
683 |
foreach ($tags as $tag) {
|
248 |
jpm |
684 |
if (in_array($tag, $metadonnees['tags'])) {
|
|
|
685 |
$correspondance = true;
|
246 |
jpm |
686 |
break;
|
|
|
687 |
}
|
|
|
688 |
}
|
248 |
jpm |
689 |
if ($correspondance) {
|
|
|
690 |
$resultat[] = $id;
|
246 |
jpm |
691 |
}
|
|
|
692 |
break;
|
|
|
693 |
default:
|
248 |
jpm |
694 |
trigger_error("Mode invalide pour la méthode analyserCache()", E_USER_WARNING);
|
246 |
jpm |
695 |
break;
|
|
|
696 |
}
|
|
|
697 |
}
|
248 |
jpm |
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
|
246 |
jpm |
703 |
} else {
|
248 |
jpm |
704 |
$resultat = array_unique(array_merge($resultat, $resultat_analyse_recursive));
|
246 |
jpm |
705 |
}
|
|
|
706 |
}
|
|
|
707 |
}
|
248 |
jpm |
708 |
return array_unique($resultat);
|
246 |
jpm |
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'];
|
248 |
jpm |
742 |
$resultat = $prefixe.'---'.$id;
|
246 |
jpm |
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++) {
|
248 |
jpm |
772 |
$chemin .= $prefixe.'--'.substr($hash, 0, $i + 1).DS;
|
246 |
jpm |
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 |
?>
|