source: spip-zone/_plugins_/cache_cool/cache_cool_options.php @ 123189

Last change on this file since 123189 was 123189, checked in by Matthieu Marcillaud, 3 months ago

Notice PHP en moins sur cache cool en mode MEMORY.

File size: 13.5 KB
Line 
1<?php
2/*
3 * Plugin Cache Cool
4 * (c) 2009 Cedric
5 * Distribue sous licence GPL
6 *
7 */
8if (!defined("_ECRIRE_INC_VERSION")) return;
9
10// ne pas faire ca en CLI, ca bloque toutes les sorties
11if (isset($_SERVER['HTTP_HOST']) and $_SERVER['HTTP_HOST']) {
12        // si deja un buffer avec une sortie on ne peut plus se lancer pour forcer le flush
13        if ($cache_cool_oblevel=ob_get_level()
14                AND $cache_cool_oblength=ob_get_length()){
15                spip_log("previous ob : $cache_cool_oblevel / previous length: $cache_cool_oblength",'cachecool'._LOG_DEBUG);
16        }
17        else {
18                spip_log("starting ob",'cachecool'._LOG_DEBUG);
19                ob_start("cache_cool_flush");
20        }
21}
22
23/**
24 * Fonction chargee de produire le cache pour un contexte et un fond donne
25 * et de le memoriser si besoin
26 *
27 * S'insere a la place de la fonction du core public_produire_page_dist pour
28 * decider si le cache existant peut etre servi froid, et lancer dans la queue
29 * une tache de mise a jour du cache en cron
30 *
31 * Dans ce cas, on reentre ici avec $use_cache.
32 * On verifie que le contexte produit un $use_cache et un $chemin_cache credibles
33 * (si on est dans l'espace prive au moment du calcul differe, aucun cache ne sera demande)
34 * Il faudrait idealement verifier qu'on retrouve le meme $chemin_cache
35 * mais cela necessiterait d'avoir le $page et le $contexte qui utilises pour cela
36 * dans le cas de la page principal dans public/assembler, mais qui ne sont pas fournis ici
37 *
38 * Si le contexte est semblable, on lance le calcul et la mise a jour du cache,
39 * sinon on reprogramme avec les memes arguments
40 *
41 * @param string $fond
42 * @param array $contexte
43 * @param int $use_cache
44 * @param string $chemin_cache
45 * @param array $contexte_cache
46 * @param array $page
47 * @param int $lastinclude
48 * @param string $connect
49 * @return array
50 */
51function public_produire_page($fond, $contexte, $use_cache, $chemin_cache, $contexte_cache, $page, &$lastinclude, $connect='', $global_context=null, $init_time = null){
52        static $processing = false;
53        $background = false;
54        if (!defined('_CACHE_COOL_CACHE_SESSION')) define('_CACHE_COOL_CACHE_SESSION',false);
55
56        // calcul differe du cache ?
57        // prend la main si
58        // - c'est un calcul normal avec mise en cache
59        // - un cache existe deja qui peut etre servi
60        // - c'est une visite anonyme (cache mutualise) OU la constante _CACHE_COOL_CACHE_SESSION a ete definie
61        // - on est pas deja en train de traiter un calcul en background
62        if ($use_cache==1 AND $chemin_cache
63                AND is_array($page) AND isset($page['texte'])
64                AND (_CACHE_COOL_CACHE_SESSION OR !isset($GLOBALS['visiteur_session']['id_auteur']) OR !$GLOBALS['visiteur_session']['id_auteur'])
65                AND !$processing
66                ) {
67                // si c'est un bot, on ne lance pas un calcul differe
68                // ca ne sert qu'a remplir la queue qui ne sera pas videe par le bot (pas de cron)
69                // mais on lui sert le cache froid tout de meme
70                if (!defined('_CACHE_COOL_REFRESH_ON_BOT')) define('_CACHE_COOL_REFRESH_ON_BOT',false);
71                if (_CACHE_COOL_REFRESH_ON_BOT OR !defined('_IS_BOT') OR !_IS_BOT){
72                        // on differe la maj du cache et on affiche le contenu du cache ce coup ci encore
73                        $where = is_null($contexte_cache)?"principal":"inclure_page";
74                        // on reprogramme avec un $use_cache=2 qui permettra de reconnaitre ces calculs
75                        $args = array($fond, $contexte, 2, $chemin_cache, $contexte_cache, array('contexte_implicite'=>$page['contexte_implicite']), $lastinclude, $connect, cache_cool_get_global_context(), $_SERVER['REQUEST_TIME']);
76
77                        // mode de fonctionnement de cache_cool : QUEUE ou MEMORY
78                        if (!defined('_CACHE_COOL_MODE')) define('_CACHE_COOL_MODE','QUEUE');
79                        if (_CACHE_COOL_MODE=="QUEUE"){
80                                job_queue_add('public_produire_page',$c="Calcul du cache $fond [$where]",$args,"",TRUE);
81                        }
82                        else {
83                                if (!isset($GLOBALS['cache_cool_queue'])){
84                                        register_shutdown_function("cache_cool_process");
85                                        $GLOBALS['cache_cool_queue'] = array();
86                                }
87                                $GLOBALS['cache_cool_queue'][] = $args;
88                        }
89                        spip_log("au frigo : $fond [$where]",'cachecool'._LOG_DEBUG);
90                }
91                gunzip_page($page); // decomprimer la page si besoin
92                #spip_log($c,'cachedelai');
93                return $page;
94        }
95
96        // si c'est un cacul differe, verifier qu'on est dans le bon contexte
97        if ($use_cache==2){
98                if ($cacher = charger_fonction('cacher','public', true)){
99                        // le nom de chemin genere ici est ignore car faux
100                        // mais il faut que l'appel produise bien un chemin
101                        // sinon pb de contexte
102                        $cacher(is_null($contexte_cache)?array():$contexte_cache, $use_cache, $chemin2, $page, $lastmodified);
103                }
104                else
105                        $use_cache = -1;
106                if (intval($use_cache)!==1 OR !$chemin2){
107                        @define('_CACHE_COOL_ABORT_DELAI',600);
108                        if (
109                                ($use_cache!=0) // le cache a deja ete mis a jour !
110                                AND ($elapsed = time()-$init_time)<_CACHE_COOL_ABORT_DELAI // cette demande est moisie
111                                ){
112                                // on n'est pas dans le bon contexte, il faut se reprogrammer !
113                                $where = is_null($contexte_cache)?"principal":"inclure_page";
114                                $args = func_get_args();
115                                job_queue_add('public_produire_page',$c="[Re$elapsed] Calcul du cache $fond [$where]",$args,"",TRUE);
116                                #spip_log($c,'cachedelai');
117                        }
118                        return;
119                }
120                if (!$processing)
121                        $processing = $background = true;
122        }
123
124        // positionner le contexte des globales si necessaire
125        if (!is_null($global_context))
126                cache_cool_global_context($global_context);
127        include_spip('public/assembler');
128        $page = public_produire_page_dist($fond, $contexte, $use_cache, $chemin_cache, $contexte_cache, $page, $lastinclude, $connect);
129       
130        if ($background){
131                if (function_exists($f='cache_cool_post_produire') OR function_exists($f=($f.'_dist'))){
132                        $f($fond, $contexte, $use_cache, $chemin_cache, $contexte_cache, $page, $lastinclude, $connect);
133                }
134        }
135       
136        // restaurer le contexte des globales si necessaire
137        if (!is_null($global_context))
138                cache_cool_global_context(false);
139
140        if ($background) $processing = false;
141
142        return $page;
143}
144
145function cache_cool_flush($content){
146        // on coupe la connection si il y a des caches a calculer
147        // (mais dommage car on perd le benefice de KeepAlive=on)
148        if (isset($GLOBALS['cache_cool_queue']) and is_array($GLOBALS['cache_cool_queue']) and $n=count($GLOBALS['cache_cool_queue'])) {
149                $close = true;
150                if (defined('_DIR_PLUGIN_MEMOIZATION')){
151                        #spip_log('meta cache_cool_action_refresh : '.$GLOBALS['meta']['cache_cool_action_refresh']." (il y a ".($_SERVER['REQUEST_TIME']-$GLOBALS['meta']['cache_cool_action_refresh'])."s)",'cachecool'._LOG_DEBUG);
152                        if (!isset($GLOBALS['meta']['cache_cool_action_refresh']) OR $GLOBALS['meta']['cache_cool_action_refresh']<$_SERVER['REQUEST_TIME']-86400){
153                                #spip_log('meta cache_cool_action_refresh_test : '.$GLOBALS['meta']['cache_cool_action_refresh_test']." (il y a ".($_SERVER['REQUEST_TIME']-$GLOBALS['meta']['cache_cool_action_refresh_test'])."s)",'cachecool'._LOG_DEBUG);
154                                if (!isset($GLOBALS['meta']['cache_cool_action_refresh_test']) OR $GLOBALS['meta']['cache_cool_action_refresh_test']<$_SERVER['REQUEST_TIME']-86400){
155                                        ecrire_meta('cache_cool_action_refresh_test',$_SERVER['REQUEST_TIME']);
156                                        $url = generer_url_action('cache_cool_refresh','',true);
157                                        if (strncmp($url,'http',4)!==0){
158                                                if (!function_exists('url_absolue')) include_spip('inc/filtres_mini');
159                                                $url = url_absolue($url);
160                                        }
161                                        cache_cool_async_curl($url);
162                                        spip_log("Test mise a jour cache async $url",'cachecool'._LOG_DEBUG);
163                                }
164                        }
165                        else{
166                                if (!function_exists('cache_set')) include_spip('inc/memoization');
167                                $id = md5($GLOBALS['ip'].self().@getmypid().time().serialize($GLOBALS['visiteur_session']));
168                                if (cache_set("cachecool-$id",$GLOBALS['cache_cool_queue'])){
169                                        $url = generer_url_action('cache_cool_refresh',"id=$id",true);
170                                        if (strncmp($url,'http',4)!==0){
171                                                if (!function_exists('url_absolue')) include_spip('inc/filtres_mini');
172                                                $url = url_absolue($url);
173                                        }
174                                        if (cache_cool_async_curl($url)){
175                                                unset($GLOBALS['cache_cool_queue']);
176                                                $close = false;
177                                                spip_log("Mise a jour $n cache lancee en async sur $url",'cachecool'._LOG_DEBUG);
178                                        }
179                                }
180                                else {
181                                        spip_log("cache_set('cachecool-$id') return false",'cachecool');
182                                }
183                        }
184                }
185                if ($close){
186                        header("X-Cache-Cool: $n");
187                        header("Content-Length: ".($l=ob_get_length()));
188                        header("Connection: close");
189                        spip_log("Connection: close (length $l) ($n cache a calculer)",'cachecool'._LOG_DEBUG);
190                }
191        }
192        return $content;
193}
194
195function cache_cool_process($force=false){
196        if (isset($GLOBALS['cache_cool_queue']) AND is_array($GLOBALS['cache_cool_queue'])){
197          // se remettre dans le bon dossier, car Apache le change parfois (toujours?)
198                chdir(_ROOT_CWD);
199                if (!$force){
200                        $flush_level = ob_get_level();
201                        // forcer le flush des tampons pas envoyes (declenche le content-length/conection:close envoye dans cache_cool_flush)
202                        while ($flush_level--) ob_end_flush();
203                        flush();
204                        if (function_exists('fastcgi_finish_request'))
205                                fastcgi_finish_request();
206                }
207
208                while (is_array($GLOBALS['cache_cool_queue'])
209                        AND $args = array_shift($GLOBALS['cache_cool_queue'])){
210                        spip_log("calcul en fin de hit public_produire_page($args[0],$args[1],$args[2],$args[3],$args[4],$args[5],$args[6],$args[7],$args[8],$args[9])",'cachecool'._LOG_DEBUG);
211                        public_produire_page($args[0],$args[1],$args[2],$args[3],$args[4],$args[5],$args[6],$args[7],$args[8],$args[9]);
212                }
213        }
214}
215
216/**
217 * Definir un nouveau contexte de globales (en sauvegardant l'ancien),
218 * ou restaurer l'ancien contexte avec la valeur false
219 * @staticvar array $pile
220 * @param array/bool $push
221 */
222function cache_cool_global_context($push){
223        static $pile = array();
224        // restaurer le contexte
225        if ($push===false AND count($pile)) {
226                $pull = array_shift($pile);
227                lang_select();
228                cache_cool_set_global_contexte($pull);
229        }
230        // definir un nouveau contexte
231        else {
232                // on empile le contexte actuel
233                array_unshift($pile, cache_cool_get_global_context());
234                // et on le modifie en commencant par la langue courante
235                lang_select($push['spip_lang']);
236                cache_cool_set_global_contexte($push);
237        }
238}
239
240/**
241 * Lire les globales utilisees implicitement dans le calcul des
242 * squelettes, et retourner un tableau les contenant
243 *
244 * @return array
245 */
246function cache_cool_get_global_context(){
247        $contexte = array();
248        $globals_to_save = array(
249                'spip_lang',
250                'visiteur_session',
251                'auteur_session',
252                'marqueur',
253                'marqueur_skel',
254                'dossier_squelettes',
255                '_COOKIE',
256                '_SERVER',
257                '_GET',
258                '_REQUEST',
259                'profondeur_url',
260                'REQUEST_URI',
261                'REQUEST_METHOD',
262        );
263        if (defined('_CACHE_COOL_GLOBALS_TO_SAVE')) {
264                $globals_to_save = array_merge($globals_to_save, explode(',', _CACHE_COOL_GLOBALS_TO_SAVE));
265                $globals_to_save = array_filter($globals_to_save);
266        }
267
268        foreach($globals_to_save as $v) {
269                $contexte[$v] = (isset($GLOBALS[$v])?$GLOBALS[$v]:null);
270        }
271        $contexte['url_de_base'] = url_de_base(false);
272        $contexte['nettoyer_uri'] = nettoyer_uri();
273        return $contexte;
274}
275
276/**
277 * Assigner les globales fournies par $c
278 * @param array $c
279 * @return void
280 */
281function cache_cool_set_global_contexte($c){
282        if (!is_array($c)) return; // ne rien faire
283        // precaution : spip_lang ne peut etre affecte brutalement
284        // il faut passer par lang_select()
285        unset($c['spip_lang']);
286       
287        url_de_base($c['url_de_base']); unset($c['url_de_base']);
288        nettoyer_uri($c['nettoyer_uri']); unset($c['nettoyer_uri']);
289        foreach($c as $k=>$v){
290                $GLOBALS[$k] = $v;
291        }
292        foreach(array(
293                'HTTP_SERVER_VARS'=>'_SERVER',
294                'HTTP_GET_VARS'=>'_GET',
295                'HTTP_COOKIE_VARS'=>'_COOKIE',
296                ) as $k1=>$k2){
297                $GLOBALS[$k1] = $GLOBALS[$k2];
298        }
299}
300
301/**
302 * Un curl async
303 * @param $url
304 * @return bool
305 */
306function cache_cool_async_curl($url){
307        #spip_log("cache_cool_async_curl $url","cachecool" . _LOG_DEBUG);
308
309        // methode la plus rapide :
310        // Si fsockopen est possible, on lance le cron via un socket en asynchrone
311        // si fsockopen echoue (disponibilite serveur, firewall) on essaye pas cURL
312        // car on a toutes les chances d'echouer pareil mais sans moyen de le savoir
313        // on passe direct a la methode background-image
314        if(function_exists('fsockopen')){
315                $parts=parse_url($url);
316                spip_log("cache_cool_async_curl avec fsockopen ".json_encode($parts),"cachecool" . _LOG_DEBUG);
317                switch ($parts['scheme']) {
318                        case 'https':
319                                $scheme = 'ssl://';
320                                $port = 443;
321                                break;
322                        case 'http':
323                        default:
324                                $scheme = '';
325                                $port = 80;
326                }
327                $fp = @fsockopen($scheme . $parts['host'], isset($parts['port']) ? $parts['port'] : $port, $errno, $errstr, 1);
328                if ($fp) {
329                        $host_sent = $parts['host'];
330                        if (isset($parts['port']) and $parts['port'] !== $port) {
331                                $host_sent .= ':' . $parts['port'];
332                        }
333                        $timeout = 200; // ms
334                        stream_set_timeout($fp, 0, $timeout * 1000);
335                        $query = $parts['path'].($parts['query']?"?".$parts['query']:"");
336                        $out = "GET ".$query." HTTP/1.1\r\n";
337                        $out.= "Host: ".$host_sent."\r\n";
338                        $out.= "Connection: Close\r\n\r\n";
339                        fwrite($fp, $out);
340                        spip_timer('cache_cool_async_curl');
341                        $t = 0;
342                        // on lit la reponse si possible pour fermer proprement la connexion
343                        // avec un timeout total de 200ms pour ne pas se bloquer
344                        while (!feof($fp) and $t < $timeout) {
345                                @fgets($fp, 1024);
346                                $t += spip_timer('cache_cool_async_curl', true);
347                                spip_timer('cache_cool_async_curl');
348                        }
349                        fclose($fp);
350                        return true;
351                }
352        }
353        // ici lancer le cron par un CURL asynchrone si CURL est present
354        elseif (function_exists("curl_init")){
355                spip_log("cache_cool_async_curl avec curl $url","cachecool" . _LOG_DEBUG);
356                //setting the curl parameters.
357                $ch = curl_init($url);
358                curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
359                // cf bug : http://www.php.net/manual/en/function.curl-setopt.php#104597
360                curl_setopt($ch, CURLOPT_NOSIGNAL, 1);
361                // valeur mini pour que la requete soit lancee
362                curl_setopt($ch, CURLOPT_TIMEOUT_MS, 100);
363                curl_exec($ch);
364                curl_close($ch);
365                return true;
366        }
367        return false;
368}
Note: See TracBrowser for help on using the repository browser.