PageRenderTime 49ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/ecrire/public/cacher.php

https://bitbucket.org/re_al_/real.test.spip
PHP | 442 lines | 230 code | 54 blank | 158 comment | 51 complexity | 963857b1026eb1d0f781418478c7a767 MD5 | raw file
Possible License(s): LGPL-2.1, MIT
  1. <?php
  2. /***************************************************************************\
  3. * SPIP, Systeme de publication pour l'internet *
  4. * *
  5. * Copyright (c) 2001-2017 *
  6. * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
  7. * *
  8. * Ce programme est un logiciel libre distribue sous licence GNU/GPL. *
  9. * Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. *
  10. \***************************************************************************/
  11. if (!defined('_ECRIRE_INC_VERSION')) {
  12. return;
  13. }
  14. /**
  15. * Le format souhaite : tmp/cache/ab/cd
  16. * soit au maximum 16^4 fichiers dans 256 repertoires
  17. * Attention a modifier simultanement le sanity check de
  18. * la fonction retire_cache() de inc/invalideur
  19. *
  20. * http://code.spip.net/@generer_nom_fichier_cache
  21. *
  22. * @param array $contexte
  23. * @param array $page
  24. * @return string
  25. */
  26. function generer_nom_fichier_cache($contexte, $page) {
  27. $u = md5(var_export(array($contexte, $page), true));
  28. return $u . ".cache";
  29. }
  30. /**
  31. * ecrire le cache dans un casier
  32. *
  33. * @param string $nom_cache
  34. * @param $valeur
  35. * @return bool
  36. */
  37. function ecrire_cache($nom_cache, $valeur) {
  38. $d = substr($nom_cache, 0, 2);
  39. $u = substr($nom_cache, 2, 2);
  40. $rep = _DIR_CACHE;
  41. $rep = sous_repertoire($rep, '', false, true);
  42. $rep = sous_repertoire($rep, $d, false, true);
  43. return ecrire_fichier($rep . $u . ".cache", serialize(array("nom_cache" => $nom_cache, "valeur" => $valeur)));
  44. }
  45. /**
  46. * lire le cache depuis un casier
  47. *
  48. * @param string $nom_cache
  49. * @return mixed
  50. */
  51. function lire_cache($nom_cache) {
  52. $d = substr($nom_cache, 0, 2);
  53. $u = substr($nom_cache, 2, 2);
  54. if (file_exists($f = _DIR_CACHE . "$d/$u.cache")
  55. and lire_fichier($f, $tmp)
  56. and $tmp = unserialize($tmp)
  57. and $tmp['nom_cache'] == $nom_cache
  58. and isset($tmp['valeur'])
  59. ) {
  60. return $tmp['valeur'];
  61. }
  62. return false;
  63. }
  64. // Parano : on signe le cache, afin d'interdire un hack d'injection
  65. // dans notre memcache
  66. function cache_signature(&$page) {
  67. if (!isset($GLOBALS['meta']['cache_signature'])) {
  68. include_spip('inc/acces');
  69. include_spip('auth/sha256.inc');
  70. ecrire_meta('cache_signature',
  71. _nano_sha256($_SERVER["DOCUMENT_ROOT"] . $_SERVER["SERVER_SIGNATURE"] . creer_uniqid()), 'non');
  72. }
  73. return crc32($GLOBALS['meta']['cache_signature'] . $page['texte']);
  74. }
  75. /**
  76. * Faut-il compresser ce cache ? A partir de 16ko ca vaut le coup
  77. * (pas de passage par reference car on veut conserver la version non compressee
  78. * pour l'afficher)
  79. * on positionne un flag gz si on comprime, pour savoir si on doit decompresser ou pas
  80. * http://code.spip.net/@gzip_page
  81. *
  82. * @param array $page
  83. * @return array
  84. */
  85. function gzip_page($page) {
  86. if (function_exists('gzcompress') and strlen($page['texte']) > 16 * 1024) {
  87. $page['gz'] = true;
  88. $page['texte'] = gzcompress($page['texte']);
  89. } else {
  90. $page['gz'] = false;
  91. }
  92. return $page;
  93. }
  94. /**
  95. * Faut-il decompresser ce cache ?
  96. * (passage par reference pour alleger)
  97. * on met a jour le flag gz quand on decompresse, pour ne pas risquer
  98. * de decompresser deux fois de suite un cache (ce qui echoue)
  99. *
  100. * http://code.spip.net/@gunzip_page
  101. *
  102. * @param array $page
  103. * @return void
  104. */
  105. function gunzip_page(&$page) {
  106. if ($page['gz']) {
  107. $page['texte'] = gzuncompress($page['texte']);
  108. $page['gz'] = false; // ne pas gzuncompress deux fois une meme page
  109. }
  110. }
  111. /**
  112. * gestion des delais d'expiration du cache...
  113. * $page passee par reference pour accelerer
  114. *
  115. * @param array $page
  116. * @param int $date
  117. * @return int
  118. * 1 si il faut mettre le cache a jour
  119. * 0 si le cache est valide
  120. * -1 si il faut calculer sans stocker en cache
  121. */
  122. /// http://code.spip.net/@cache_valide
  123. function cache_valide(&$page, $date) {
  124. $now = $_SERVER['REQUEST_TIME'];
  125. // Apparition d'un nouvel article post-date ?
  126. if (isset($GLOBALS['meta']['post_dates'])
  127. and $GLOBALS['meta']['post_dates'] == 'non'
  128. and isset($GLOBALS['meta']['date_prochain_postdate'])
  129. and $now > $GLOBALS['meta']['date_prochain_postdate']
  130. ) {
  131. spip_log('Un article post-date invalide le cache');
  132. include_spip('inc/rubriques');
  133. calculer_prochain_postdate(true);
  134. }
  135. if (defined('_VAR_NOCACHE') and _VAR_NOCACHE) {
  136. return -1;
  137. }
  138. if (isset($GLOBALS['meta']['cache_inhib']) and $_SERVER['REQUEST_TIME'] < $GLOBALS['meta']['cache_inhib']) {
  139. return -1;
  140. }
  141. if (defined('_NO_CACHE')) {
  142. return (_NO_CACHE == 0 and !isset($page['texte'])) ? 1 : _NO_CACHE;
  143. }
  144. // pas de cache ? on le met a jour, sauf pour les bots (on leur calcule la page sans mise en cache)
  145. if (!$page or !isset($page['texte']) or !isset($page['entetes']['X-Spip-Cache'])) {
  146. return _IS_BOT ? -1 : 1;
  147. }
  148. // controle de la signature
  149. if ($page['sig'] !== cache_signature($page)) {
  150. return _IS_BOT ? -1 : 1;
  151. }
  152. // #CACHE{n,statique} => on n'invalide pas avec derniere_modif
  153. // cf. ecrire/public/balises.php, balise_CACHE_dist()
  154. if (!isset($page['entetes']['X-Spip-Statique']) or $page['entetes']['X-Spip-Statique'] !== 'oui') {
  155. // Cache invalide par la meta 'derniere_modif'
  156. // sauf pour les bots, qui utilisent toujours le cache
  157. if (!_IS_BOT
  158. and $GLOBALS['derniere_modif_invalide']
  159. and isset($GLOBALS['meta']['derniere_modif'])
  160. and $date < $GLOBALS['meta']['derniere_modif']
  161. ) {
  162. return 1;
  163. }
  164. }
  165. // Sinon comparer l'age du fichier a sa duree de cache
  166. $duree = intval($page['entetes']['X-Spip-Cache']);
  167. $cache_mark = (isset($GLOBALS['meta']['cache_mark']) ? $GLOBALS['meta']['cache_mark'] : 0);
  168. if ($duree == 0) #CACHE{0}
  169. {
  170. return -1;
  171. } // sauf pour les bots, qui utilisent toujours le cache
  172. else {
  173. if ((!_IS_BOT and $date + $duree < $now)
  174. # le cache est anterieur a la derniere purge : l'ignorer, meme pour les bots
  175. or $date < $cache_mark
  176. ) {
  177. return _IS_BOT ? -1 : 1;
  178. } else {
  179. return 0;
  180. }
  181. }
  182. }
  183. /**
  184. * Creer le fichier cache
  185. * Passage par reference de $page par souci d'economie
  186. *
  187. * http://code.spip.net/@creer_cache
  188. *
  189. * @param array $page
  190. * @param string $chemin_cache
  191. * @return void
  192. */
  193. function creer_cache(&$page, &$chemin_cache) {
  194. // Ne rien faire si on est en preview, debug, ou si une erreur
  195. // grave s'est presentee (compilation du squelette, MySQL, etc)
  196. // le cas var_nocache ne devrait jamais arriver ici (securite)
  197. // le cas spip_interdire_cache correspond a une ereur SQL grave non anticipable
  198. if ((defined('_VAR_NOCACHE') and _VAR_NOCACHE)
  199. or defined('spip_interdire_cache')
  200. ) {
  201. return;
  202. }
  203. // Si la page c1234 a un invalideur de session 'zz', sauver dans
  204. // 'tmp/cache/MD5(chemin_cache)_zz'
  205. if (isset($page['invalideurs'])
  206. and isset($page['invalideurs']['session'])
  207. ) {
  208. // on verifie que le contenu du chemin cache indique seulement
  209. // "cache sessionne" ; sa date indique la date de validite
  210. // des caches sessionnes
  211. if (!$tmp = lire_cache($chemin_cache)) {
  212. spip_log('Creation cache sessionne ' . $chemin_cache);
  213. $tmp = array(
  214. 'invalideurs' => array('session' => ''),
  215. 'lastmodified' => $_SERVER['REQUEST_TIME']
  216. );
  217. ecrire_cache($chemin_cache, $tmp);
  218. }
  219. $chemin_cache = generer_nom_fichier_cache(array("chemin_cache" => $chemin_cache),
  220. array("session" => $page['invalideurs']['session']));
  221. }
  222. // ajouter la date de production dans le cache lui meme
  223. // (qui contient deja sa duree de validite)
  224. $page['lastmodified'] = $_SERVER['REQUEST_TIME'];
  225. // compresser le contenu si besoin
  226. $pagez = gzip_page($page);
  227. // signer le contenu
  228. $pagez['sig'] = cache_signature($pagez);
  229. // l'enregistrer, compresse ou non...
  230. $ok = ecrire_cache($chemin_cache, $pagez);
  231. spip_log((_IS_BOT ? "Bot:" : "") . "Creation du cache $chemin_cache pour "
  232. . $page['entetes']['X-Spip-Cache'] . " secondes" . ($ok ? '' : ' (erreur!)'), _LOG_INFO_IMPORTANTE);
  233. // Inserer ses invalideurs
  234. include_spip('inc/invalideur');
  235. maj_invalideurs($chemin_cache, $page);
  236. }
  237. /**
  238. * purger un petit cache (tidy ou recherche) qui ne doit pas contenir de
  239. * vieux fichiers ; (cette fonction ne sert que dans des plugins obsoletes)
  240. *
  241. * http://code.spip.net/@nettoyer_petit_cache
  242. *
  243. * @param string $prefix
  244. * @param int $duree
  245. * @return void
  246. */
  247. function nettoyer_petit_cache($prefix, $duree = 300) {
  248. // determiner le repertoire a purger : 'tmp/CACHE/rech/'
  249. $dircache = sous_repertoire(_DIR_CACHE, $prefix);
  250. if (spip_touch($dircache . 'purger_' . $prefix, $duree, true)) {
  251. foreach (preg_files($dircache, '[.]txt$') as $f) {
  252. if ($_SERVER['REQUEST_TIME'] - (@file_exists($f) ? @filemtime($f) : 0) > $duree) {
  253. spip_unlink($f);
  254. }
  255. }
  256. }
  257. }
  258. /**
  259. * Interface du gestionnaire de cache
  260. * Si son 3e argument est non vide, elle passe la main a creer_cache
  261. * Sinon, elle recoit un contexte (ou le construit a partir de REQUEST_URI)
  262. * et affecte les 4 autres parametres recus par reference:
  263. * - use_cache qui vaut
  264. * -1 s'il faut calculer la page sans la mettre en cache
  265. * 0 si on peut utiliser un cache existant
  266. * 1 s'il faut calculer la page et la mettre en cache
  267. * - chemin_cache qui est le chemin d'acces au fichier ou vide si pas cachable
  268. * - page qui est le tableau decrivant la page, si le cache la contenait
  269. * - lastmodified qui vaut la date de derniere modif du fichier.
  270. * Elle retourne '' si tout va bien
  271. * un message d'erreur si le calcul de la page est totalement impossible
  272. *
  273. * http://code.spip.net/@public_cacher_dist
  274. *
  275. * @param array $contexte
  276. * @param int $use_cache
  277. * @param string $chemin_cache
  278. * @param array $page
  279. * @param int $lastmodified
  280. * @return string|void
  281. */
  282. function public_cacher_dist($contexte, &$use_cache, &$chemin_cache, &$page, &$lastmodified) {
  283. # fonction de cache minimale : dire "non on ne met rien en cache"
  284. # $use_cache = -1; return;
  285. // Second appel, destine a l'enregistrement du cache sur le disque
  286. if (isset($chemin_cache)) {
  287. return creer_cache($page, $chemin_cache);
  288. }
  289. // Toute la suite correspond au premier appel
  290. $contexte_implicite = $page['contexte_implicite'];
  291. // Cas ignorant le cache car completement dynamique
  292. if ($_SERVER['REQUEST_METHOD'] == 'POST'
  293. or _request('connect')
  294. ) {
  295. $use_cache = -1;
  296. $lastmodified = 0;
  297. $chemin_cache = "";
  298. $page = array();
  299. return;
  300. }
  301. // Controler l'existence d'un cache nous correspondant
  302. $chemin_cache = generer_nom_fichier_cache($contexte, $page);
  303. $lastmodified = 0;
  304. // charger le cache s'il existe (et si il a bien le bon hash = anticollision)
  305. if (!$page = lire_cache($chemin_cache)) {
  306. $page = array();
  307. }
  308. // s'il est sessionne, charger celui correspondant a notre session
  309. if (isset($page['invalideurs'])
  310. and isset($page['invalideurs']['session'])
  311. ) {
  312. $chemin_cache_session = generer_nom_fichier_cache(array("chemin_cache" => $chemin_cache),
  313. array("session" => spip_session()));
  314. if ($page_session = lire_cache($chemin_cache_session)
  315. and $page_session['lastmodified'] >= $page['lastmodified']
  316. ) {
  317. $page = $page_session;
  318. } else {
  319. $page = array();
  320. }
  321. }
  322. // Faut-il effacer des pages invalidees (en particulier ce cache-ci) ?
  323. if (isset($GLOBALS['meta']['invalider'])) {
  324. // ne le faire que si la base est disponible
  325. if (spip_connect()) {
  326. include_spip('inc/invalideur');
  327. retire_caches($chemin_cache); # API invalideur inutile
  328. supprimer_fichier(_DIR_CACHE . $chemin_cache);
  329. if (isset($chemin_cache_session) and $chemin_cache_session) {
  330. supprimer_fichier(_DIR_CACHE . $chemin_cache_session);
  331. }
  332. }
  333. }
  334. // Si un calcul, recalcul [ou preview, mais c'est recalcul] est demande,
  335. // on supprime le cache
  336. if (defined('_VAR_MODE') && _VAR_MODE &&
  337. (isset($_COOKIE['spip_session'])
  338. || isset($_COOKIE['spip_admin'])
  339. || @file_exists(_ACCESS_FILE_NAME))
  340. ) {
  341. $page = array('contexte_implicite' => $contexte_implicite); // ignorer le cache deja lu
  342. include_spip('inc/invalideur');
  343. retire_caches($chemin_cache); # API invalideur inutile
  344. supprimer_fichier(_DIR_CACHE . $chemin_cache);
  345. if (isset($chemin_cache_session) and $chemin_cache_session) {
  346. supprimer_fichier(_DIR_CACHE . $chemin_cache_session);
  347. }
  348. }
  349. // $delais par defaut
  350. // pour toutes les pages sans #CACHE{} hors modeles/ et espace privé
  351. // qui sont a cache nul par defaut
  352. if (!isset($GLOBALS['delais'])) {
  353. if (!defined('_DUREE_CACHE_DEFAUT')) {
  354. define('_DUREE_CACHE_DEFAUT', 24 * 3600);
  355. }
  356. $GLOBALS['delais'] = _DUREE_CACHE_DEFAUT;
  357. }
  358. // determiner la validite de la page
  359. if ($page) {
  360. $use_cache = cache_valide($page, isset($page['lastmodified']) ? $page['lastmodified'] : 0);
  361. // le contexte implicite n'est pas stocke dans le cache, mais il y a equivalence
  362. // par le nom du cache. On le reinjecte donc ici pour utilisation eventuelle au calcul
  363. $page['contexte_implicite'] = $contexte_implicite;
  364. if (!$use_cache) {
  365. // $page est un cache utilisable
  366. gunzip_page($page);
  367. return;
  368. }
  369. } else {
  370. $page = array('contexte_implicite' => $contexte_implicite);
  371. $use_cache = cache_valide($page, 0); // fichier cache absent : provoque le calcul
  372. }
  373. // Si pas valide mais pas de connexion a la base, le garder quand meme
  374. if (!spip_connect()) {
  375. if (isset($page['texte'])) {
  376. gunzip_page($page);
  377. $use_cache = 0;
  378. } else {
  379. spip_log("Erreur base de donnees, impossible utiliser $chemin_cache");
  380. include_spip('inc/minipres');
  381. return minipres(_T('info_travaux_titre'), _T('titre_probleme_technique'), array('status' => 503));
  382. }
  383. }
  384. if ($use_cache < 0) {
  385. $chemin_cache = '';
  386. }
  387. return;
  388. }