Completed
Push — master ( 1f4ad4...6aa332 )
by cam
06:01 queued 20s
created

cacher.php ➔ cache_chemin_fichier()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
nc 6
nop 2
dl 0
loc 22
rs 9.568
c 0
b 0
f 0
1
<?php
2
3
/***************************************************************************\
4
 *  SPIP, Système de publication pour l'internet                           *
5
 *                                                                         *
6
 *  Copyright © avec tendresse depuis 2001                                 *
7
 *  Arnaud Martin, Antoine Pitrou, Philippe Rivière, Emmanuel Saint-James  *
8
 *                                                                         *
9
 *  Ce programme est un logiciel libre distribué sous licence GNU/GPL.     *
10
 *  Pour plus de détails voir le fichier COPYING.txt ou l'aide en ligne.   *
11
\***************************************************************************/
12
13
if (!defined('_ECRIRE_INC_VERSION')) {
14
	return;
15
}
16
17
/**
18
 * Le format souhaite : tmp/cache/ab/cd
19
 * soit au maximum 16^4 fichiers dans 256 repertoires
20
 * Attention a modifier simultanement le sanity check de
21
 * la fonction retire_cache() de inc/invalideur
22
 *
23
 * https://code.spip.net/@generer_nom_fichier_cache
24
 *
25
 * @param array $contexte
26
 * @param array $page
27
 * @return string
28
 */
29
function generer_nom_fichier_cache($contexte, $page) {
30
	$u = md5(var_export(array($contexte, $page), true));
31
32
	return $u . ".cache";
33
}
34
35
/**
36
 * Calcule le chemin hashe du fichier cache
37
 * Le format souhaite : tmp/cache/ab/cd
38
 * soit au maximum 16^4 fichiers dans 256 repertoires
39
 * mais la longueur est configurable via un define qui permer d'avoir une taille de 16^_CACHE_PROFONDEUR_STOCKAGE
40
 *
41
 * Attention a modifier simultanement le sanity check de
42
 * la fonction retire_cache() de inc/invalideur
43
 *
44
 * @param $nom_cache
45
 * @return string
46
 */
47
function cache_chemin_fichier($nom_cache, $ecrire = false) {
48
	static $l1, $l2;
49
	if (is_null($l1)) {
50
		$length = (defined('_CACHE_PROFONDEUR_STOCKAGE') ? min(8,max(_CACHE_PROFONDEUR_STOCKAGE,2)) : 4);
51
		$l1 = intval(floor($length / 2));
52
		$l2 = $length - $l1;
53
	}
54
	$d = substr($nom_cache, 0, $l1);
55
	$u = substr($nom_cache, $l1, $l2);
56
57
	if ($ecrire) {
58
		$rep = sous_repertoire(_DIR_CACHE, '', false, true);
59
		$rep = sous_repertoire($rep, 'calcul/', false, true);
60
		$rep = sous_repertoire($rep, $d, false, true);
61
	}
62
	else {
63
		// en lecture on essaye pa de creer les repertoires, on va au plus vite
64
		$rep = _DIR_CACHE . "$d/";
65
	}
66
67
	return $rep . $u . ".cache";
68
}
69
70
/**
71
 * ecrire le cache dans un casier
72
 *
73
 * @param string $nom_cache
74
 * @param $valeur
75
 * @return bool
76
 */
77
function ecrire_cache($nom_cache, $valeur) {
78
	return ecrire_fichier(cache_chemin_fichier($nom_cache, true), serialize(array("nom_cache" => $nom_cache, "valeur" => $valeur)));
79
}
80
81
/**
82
 * lire le cache depuis un casier
83
 *
84
 * @param string $nom_cache
85
 * @return mixed
86
 */
87
function lire_cache($nom_cache) {
88
	if (file_exists($f = cache_chemin_fichier($nom_cache))
89
		and lire_fichier($f, $tmp)
90
		and $tmp = unserialize($tmp)
91
		and $tmp['nom_cache'] == $nom_cache
92
		and isset($tmp['valeur'])
93
	) {
94
		return $tmp['valeur'];
95
	}
96
97
	return false;
98
}
99
100
// Parano : on signe le cache, afin d'interdire un hack d'injection
101
// dans notre memcache
102
function cache_signature(&$page) {
103
	if (!isset($GLOBALS['meta']['cache_signature'])) {
104
		include_spip('inc/acces');
105
		include_spip('auth/sha256.inc');
106
		ecrire_meta('cache_signature',
107
			spip_sha256(
108
				$_SERVER["DOCUMENT_ROOT"] 
109
				. (isset($_SERVER['SERVER_SIGNATURE']) ? $_SERVER["SERVER_SIGNATURE"] : "")
110
				. creer_uniqid()
111
			), 'non');
112
	}
113
114
	return crc32($GLOBALS['meta']['cache_signature'] . $page['texte']);
115
}
116
117
/**
118
 * Faut-il compresser ce cache ? A partir de 16ko ca vaut le coup
119
 * (pas de passage par reference car on veut conserver la version non compressee
120
 * pour l'afficher)
121
 * on positionne un flag gz si on comprime, pour savoir si on doit decompresser ou pas
122
 * https://code.spip.net/@gzip_page
123
 *
124
 * @param array $page
125
 * @return array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,boolean|string>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
126
 */
127
function gzip_page($page) {
128
	if (function_exists('gzcompress') and strlen($page['texte']) > 16 * 1024) {
129
		$page['gz'] = true;
130
		$page['texte'] = gzcompress($page['texte']);
131
	} else {
132
		$page['gz'] = false;
133
	}
134
135
	return $page;
136
}
137
138
/**
139
 * Faut-il decompresser ce cache ?
140
 * (passage par reference pour alleger)
141
 * on met a jour le flag gz quand on decompresse, pour ne pas risquer
142
 * de decompresser deux fois de suite un cache (ce qui echoue)
143
 *
144
 * https://code.spip.net/@gunzip_page
145
 *
146
 * @param array $page
147
 * @return void
148
 */
149
function gunzip_page(&$page) {
150
	if ($page['gz']) {
151
		$page['texte'] = gzuncompress($page['texte']);
152
		$page['gz'] = false; // ne pas gzuncompress deux fois une meme page
153
	}
154
}
155
156
/**
157
 * gestion des delais d'expiration du cache...
158
 * $page passee par reference pour accelerer
159
 *
160
 * @param array $page
161
 * @param int $date
162
 * @return int
163
 * 1 si il faut mettre le cache a jour
164
 * 0 si le cache est valide
165
 * -1 si il faut calculer sans stocker en cache
166
 */
167
/// https://code.spip.net/@cache_valide
168
function cache_valide(&$page, $date) {
169
	$now = $_SERVER['REQUEST_TIME'];
170
171
	// Apparition d'un nouvel article post-date ?
172
	if (isset($GLOBALS['meta']['post_dates'])
173
		and $GLOBALS['meta']['post_dates'] == 'non'
174
		and isset($GLOBALS['meta']['date_prochain_postdate'])
175
		and $now > $GLOBALS['meta']['date_prochain_postdate']
176
	) {
177
		spip_log('Un article post-date invalide le cache');
178
		include_spip('inc/rubriques');
179
		calculer_prochain_postdate(true);
180
	}
181
182
	if (defined('_VAR_NOCACHE') and _VAR_NOCACHE) {
183
		return -1;
184
	}
185
	if (isset($GLOBALS['meta']['cache_inhib']) and $_SERVER['REQUEST_TIME'] < $GLOBALS['meta']['cache_inhib']) {
186
		return -1;
187
	}
188
	if (defined('_NO_CACHE')) {
189
		return (_NO_CACHE == 0 and !isset($page['texte'])) ? 1 : _NO_CACHE;
190
	}
191
192
	// pas de cache ? on le met a jour, sauf pour les bots (on leur calcule la page sans mise en cache)
193
	if (!$page or !isset($page['texte']) or !isset($page['entetes']['X-Spip-Cache'])) {
194
		return _IS_BOT ? -1 : 1;
195
	}
196
197
	// controle de la signature
198
	if ($page['sig'] !== cache_signature($page)) {
199
		return _IS_BOT ? -1 : 1;
200
	}
201
202
	// #CACHE{n,statique} => on n'invalide pas avec derniere_modif
203
	// cf. ecrire/public/balises.php, balise_CACHE_dist()
204
	if (!isset($page['entetes']['X-Spip-Statique']) or $page['entetes']['X-Spip-Statique'] !== 'oui') {
205
206
		// Cache invalide par la meta 'derniere_modif'
207
		// sauf pour les bots, qui utilisent toujours le cache
208
		if (!_IS_BOT
209
			and $GLOBALS['derniere_modif_invalide']
210
			and isset($GLOBALS['meta']['derniere_modif'])
211
			and $date < $GLOBALS['meta']['derniere_modif']
212
		) {
213
			return 1;
214
		}
215
216
	}
217
218
	// Sinon comparer l'age du fichier a sa duree de cache
219
	$duree = intval($page['entetes']['X-Spip-Cache']);
220
	$cache_mark = (isset($GLOBALS['meta']['cache_mark']) ? $GLOBALS['meta']['cache_mark'] : 0);
221
	if ($duree == 0)  #CACHE{0}
222
	{
223
		return -1;
224
	} // sauf pour les bots, qui utilisent toujours le cache
225
	else {
226
		if ((!_IS_BOT and $date + $duree < $now)
227
			# le cache est anterieur a la derniere purge : l'ignorer, meme pour les bots
228
			or $date < $cache_mark
229
		) {
230
			return _IS_BOT ? -1 : 1;
231
		} else {
232
			return 0;
233
		}
234
	}
235
}
236
237
/**
238
 * Creer le fichier cache
239
 * Passage par reference de $page par souci d'economie
240
 *
241
 * https://code.spip.net/@creer_cache
242
 *
243
 * @param array $page
244
 * @param string $chemin_cache
245
 * @return void
246
 */
247
function creer_cache(&$page, &$chemin_cache) {
248
249
	// Ne rien faire si on est en preview, debug, ou si une erreur
250
	// grave s'est presentee (compilation du squelette, MySQL, etc)
251
	// le cas var_nocache ne devrait jamais arriver ici (securite)
252
	// le cas spip_interdire_cache correspond a une ereur SQL grave non anticipable
253
	if ((defined('_VAR_NOCACHE') and _VAR_NOCACHE)
254
		or defined('spip_interdire_cache')
255
	) {
256
		return;
257
	}
258
259
	// Si la page c1234 a un invalideur de session 'zz', sauver dans
260
	// 'tmp/cache/MD5(chemin_cache)_zz'
261
	if (isset($page['invalideurs'])
262
		and isset($page['invalideurs']['session'])
263
	) {
264
		// on verifie que le contenu du chemin cache indique seulement
265
		// "cache sessionne" ; sa date indique la date de validite
266
		// des caches sessionnes
267
		if (!$tmp = lire_cache($chemin_cache)) {
268
			spip_log('Creation cache sessionne ' . $chemin_cache);
269
			$tmp = array(
270
				'invalideurs' => array('session' => ''),
271
				'lastmodified' => $_SERVER['REQUEST_TIME']
272
			);
273
			ecrire_cache($chemin_cache, $tmp);
274
		}
275
		$chemin_cache = generer_nom_fichier_cache(array("chemin_cache" => $chemin_cache),
276
			array("session" => $page['invalideurs']['session']));
277
	}
278
279
	// ajouter la date de production dans le cache lui meme
280
	// (qui contient deja sa duree de validite)
281
	$page['lastmodified'] = $_SERVER['REQUEST_TIME'];
282
283
	// compresser le contenu si besoin
284
	$pagez = gzip_page($page);
285
286
	// signer le contenu
287
	$pagez['sig'] = cache_signature($pagez);
288
289
	// l'enregistrer, compresse ou non...
290
	$ok = ecrire_cache($chemin_cache, $pagez);
291
292
	spip_log((_IS_BOT ? "Bot:" : "") . "Creation du cache $chemin_cache pour "
293
		. $page['entetes']['X-Spip-Cache'] . " secondes" . ($ok ? '' : ' (erreur!)'), _LOG_INFO);
294
295
	// Inserer ses invalideurs
296
	include_spip('inc/invalideur');
297
	maj_invalideurs($chemin_cache, $page);
298
299
}
300
301
302
/**
303
 * purger un petit cache (tidy ou recherche) qui ne doit pas contenir de
304
 * vieux fichiers ; (cette fonction ne sert que dans des plugins obsoletes)
305
 *
306
 * https://code.spip.net/@nettoyer_petit_cache
307
 *
308
 * @param string $prefix
309
 * @param int $duree
310
 * @return void
311
 */
312
function nettoyer_petit_cache($prefix, $duree = 300) {
313
	// determiner le repertoire a purger : 'tmp/CACHE/rech/'
314
	$dircache = sous_repertoire(_DIR_CACHE, $prefix);
315
	if (spip_touch($dircache . 'purger_' . $prefix, $duree, true)) {
316
		foreach (preg_files($dircache, '[.]txt$') as $f) {
317
			if ($_SERVER['REQUEST_TIME'] - (@file_exists($f) ? @filemtime($f) : 0) > $duree) {
318
				spip_unlink($f);
319
			}
320
		}
321
	}
322
}
323
324
325
/**
326
 * Interface du gestionnaire de cache
327
 * Si son 3e argument est non vide, elle passe la main a creer_cache
328
 * Sinon, elle recoit un contexte (ou le construit a partir de REQUEST_URI)
329
 * et affecte les 4 autres parametres recus par reference:
330
 * - use_cache qui vaut
331
 *     -1 s'il faut calculer la page sans la mettre en cache
332
 *      0 si on peut utiliser un cache existant
333
 *      1 s'il faut calculer la page et la mettre en cache
334
 * - chemin_cache qui est le chemin d'acces au fichier ou vide si pas cachable
335
 * - page qui est le tableau decrivant la page, si le cache la contenait
336
 * - lastmodified qui vaut la date de derniere modif du fichier.
337
 * Elle retourne '' si tout va bien
338
 * un message d'erreur si le calcul de la page est totalement impossible
339
 *
340
 * https://code.spip.net/@public_cacher_dist
341
 *
342
 * @param array $contexte
343
 * @param int $use_cache
344
 * @param string $chemin_cache
345
 * @param array $page
346
 * @param int $lastmodified
347
 * @return string|void
348
 */
349
function public_cacher_dist($contexte, &$use_cache, &$chemin_cache, &$page, &$lastmodified) {
350
351
	# fonction de cache minimale : dire "non on ne met rien en cache"
352
	# $use_cache = -1; return;
353
354
	// Second appel, destine a l'enregistrement du cache sur le disque
355
	if (isset($chemin_cache)) {
356
		return creer_cache($page, $chemin_cache);
357
	}
358
359
	// Toute la suite correspond au premier appel
360
	$contexte_implicite = $page['contexte_implicite'];
361
362
	// Cas ignorant le cache car completement dynamique
363
	if ($_SERVER['REQUEST_METHOD'] == 'POST'
364
		or _request('connect')
365
	) {
366
		$use_cache = -1;
367
		$lastmodified = 0;
368
		$chemin_cache = "";
369
		$page = array();
370
371
		return;
372
	}
373
374
	// Controler l'existence d'un cache nous correspondant
375
	$chemin_cache = generer_nom_fichier_cache($contexte, $page);
376
	$lastmodified = 0;
377
378
	// charger le cache s'il existe (et si il a bien le bon hash = anticollision)
379
	if (!$page = lire_cache($chemin_cache)) {
380
		$page = array();
381
	}
382
383
	// s'il est sessionne, charger celui correspondant a notre session
384
	if (isset($page['invalideurs'])
385
		and isset($page['invalideurs']['session'])
386
	) {
387
		$chemin_cache_session = generer_nom_fichier_cache(array("chemin_cache" => $chemin_cache),
388
			array("session" => spip_session()));
389
		if ($page_session = lire_cache($chemin_cache_session)
390
			and $page_session['lastmodified'] >= $page['lastmodified']
391
		) {
392
			$page = $page_session;
393
		} else {
394
			$page = array();
395
		}
396
	}
397
398
399
	// Faut-il effacer des pages invalidees (en particulier ce cache-ci) ?
400
	if (isset($GLOBALS['meta']['invalider'])) {
401
		// ne le faire que si la base est disponible
402
		if (spip_connect()) {
403
			include_spip('inc/invalideur');
404
			retire_caches($chemin_cache); # API invalideur inutile
405
			supprimer_fichier(_DIR_CACHE . $chemin_cache);
406
			if (isset($chemin_cache_session) and $chemin_cache_session) {
407
				supprimer_fichier(_DIR_CACHE . $chemin_cache_session);
408
			}
409
		}
410
	}
411
412
	// Si un calcul, recalcul [ou preview, mais c'est recalcul] est demande,
413
	// on supprime le cache
414
	if (defined('_VAR_MODE') && _VAR_MODE &&
415
		(isset($_COOKIE['spip_session'])
416
			|| isset($_COOKIE['spip_admin'])
417
			|| @file_exists(_ACCESS_FILE_NAME))
418
	) {
419
		$page = array('contexte_implicite' => $contexte_implicite); // ignorer le cache deja lu
420
		include_spip('inc/invalideur');
421
		retire_caches($chemin_cache); # API invalideur inutile
422
		supprimer_fichier(_DIR_CACHE . $chemin_cache);
423
		if (isset($chemin_cache_session) and $chemin_cache_session) {
424
			supprimer_fichier(_DIR_CACHE . $chemin_cache_session);
425
		}
426
	}
427
428
	// $delais par defaut
429
	// pour toutes les pages sans #CACHE{} hors modeles/ et espace privé
430
	// qui sont a cache nul par defaut
431
	if (!isset($GLOBALS['delais'])) {
432
		if (!defined('_DUREE_CACHE_DEFAUT')) {
433
			define('_DUREE_CACHE_DEFAUT', 24 * 3600);
434
		}
435
		$GLOBALS['delais'] = _DUREE_CACHE_DEFAUT;
436
	}
437
438
	// determiner la validite de la page
439
	if ($page) {
440
		$use_cache = cache_valide($page, isset($page['lastmodified']) ? $page['lastmodified'] : 0);
441
		// le contexte implicite n'est pas stocke dans le cache, mais il y a equivalence
442
		// par le nom du cache. On le reinjecte donc ici pour utilisation eventuelle au calcul
443
		$page['contexte_implicite'] = $contexte_implicite;
444
		if (!$use_cache) {
445
			// $page est un cache utilisable
446
			gunzip_page($page);
447
448
			return;
449
		}
450
	} else {
451
		$page = array('contexte_implicite' => $contexte_implicite);
452
		$use_cache = cache_valide($page, 0); // fichier cache absent : provoque le calcul
453
	}
454
455
	// Si pas valide mais pas de connexion a la base, le garder quand meme
456
	if (!spip_connect()) {
457
		if (isset($page['texte'])) {
458
			gunzip_page($page);
459
			$use_cache = 0;
460 View Code Duplication
		} else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
461
			spip_log("Erreur base de donnees, impossible utiliser $chemin_cache");
462
			include_spip('inc/minipres');
463
464
			return minipres(_T('info_travaux_titre'), _T('titre_probleme_technique'), array('status' => 503));
465
		}
466
	}
467
468
	if ($use_cache < 0) {
469
		$chemin_cache = '';
470
	}
471
472
	return;
473
}
474