[SPIP] +2.1.12
[velocampus/web/www.git] / www / extensions / compresseur / inc / compresseur.php
1 <?php
2
3 if (!defined("_ECRIRE_INC_VERSION")) return;
4
5 function compacte_ecrire_balise_script_dist($src){
6 return "<script type='text/javascript' src='$src'></script>";
7 }
8 function compacte_ecrire_balise_link_dist($src,$media=""){
9 return "<link rel='stylesheet'".($media?" media='$media'":"")." href='$src' type='text/css' />";
10 }
11
12
13 // http://doc.spip.org/@compacte_css
14 function compacte_css ($contenu) {
15 // nettoyer la css de tout ce qui sert pas
16 $contenu = preg_replace(",/\*.*\*/,Ums","",$contenu); // pas de commentaires
17 $contenu = preg_replace(",\s(?=\s),Ums","",$contenu); // pas d'espaces consecutifs
18 $contenu = preg_replace("/\s?({|;|,|:)\s?/ms","$1",$contenu); // pas d'espaces dans les declarations css
19 $contenu = preg_replace("/\s}/ms","}",$contenu); // pas d'espaces dans les declarations css
20 // passser les codes couleurs en 3 car si possible
21 // uniquement si non precedees d'un [="'] ce qui indique qu'on est dans un filter(xx=#?...)
22 $contenu = preg_replace(";([:\s,(])#([0-9a-f])(\\2)([0-9a-f])(\\4)([0-9a-f])(\\6)(?=[^\w\-]);i","$1#$2$4$6",$contenu);
23 // supprimer les declarations vides
24 $contenu = preg_replace(",(^|})([^{}]*){},Ums","$1",$contenu);
25 $contenu = trim($contenu);
26
27 return $contenu;
28 }
29
30 // Compacte du javascript grace a Dean Edward's JavaScriptPacker
31 // utile pour prive/jquery.js par exemple
32 // http://doc.spip.org/@compacte_js
33 function compacte_js($flux) {
34 // si la closure est demandee, on pourrait zapper cette etape
35 // mais avec le risque, en localhost, de depasser 200ko et d'echec
36 #if ($GLOBALS['meta']['auto_compress_closure'] == 'oui')
37 # return $flux;
38
39 if (!strlen($flux))
40 return $flux;
41 include_spip('lib/JavascriptPacker/class.JavaScriptPacker');
42 $packer = new JavaScriptPacker($flux, 0, true, false);
43
44 // en cas d'echec (?) renvoyer l'original
45 if (!strlen($t = $packer->pack())) {
46 spip_log('erreur de compacte_js');
47 return $flux;
48 }
49
50 return $t;
51 }
52
53
54 // Appelee par compacte_head() si le webmestre le desire, cette fonction
55 // compacte les scripts js dans un fichier statique pose dans local/
56 // en entree : un <head> html.
57 // http://doc.spip.org/@compacte_head_js
58 function compacte_head_js($flux) {
59 $url_base = url_de_base();
60 $url_page = substr(generer_url_public('A'), 0, -1);
61 $dir = preg_quote($url_page,',').'|'.preg_quote(preg_replace(",^$url_base,",_DIR_RACINE,$url_page),',');
62
63 $scripts = array();
64 $flux_nocomment = preg_replace(",<!--.*-->,Uims","",$flux);
65 foreach (extraire_balises($flux_nocomment,'script') as $s) {
66 if (extraire_attribut($s, 'type') === 'text/javascript'
67 AND is_null(extraire_attribut($s, 'id')) # script avec un id : pas touche
68 AND $src = extraire_attribut($s, 'src')
69 AND !strlen(strip_tags($s))
70 AND (
71 preg_match(',^('.$dir.')(.*)$,', $src, $r)
72 OR (
73 // ou si c'est un fichier
74 $src = preg_replace(',^'.preg_quote(url_de_base(),',').',', '', $src)
75 // enlever un timestamp eventuel derriere un nom de fichier statique
76 AND $src2 = preg_replace(",[.]js[?].+$,",'.js',$src)
77 // verifier qu'il n'y a pas de ../ ni / au debut (securite)
78 AND !preg_match(',(^/|\.\.),', substr($src,strlen(_DIR_RACINE)))
79 // et si il est lisible
80 AND @is_readable($src2)
81 )
82 )) {
83 if ($r)
84 $scripts[$s] = explode('&',
85 str_replace('&amp;', '&', $r[2]), 2);
86 else
87 $scripts[$s] = $src;
88 }
89 }
90
91 if (list($src,$comms) = filtre_cache_static($scripts,'js')){
92 $compacte_ecrire_balise_script = charger_fonction('compacte_ecrire_balise_script','');
93 $scripts = array_keys($scripts);
94 $flux = str_replace(reset($scripts),
95 $comms .$compacte_ecrire_balise_script($src)."\n",
96 $flux);
97 $flux = str_replace($scripts,"",$flux);
98 }
99
100 return $flux;
101 }
102
103 // Appelee par compacte_head() si le webmestre le desire, cette fonction
104 // compacte les feuilles de style css dans un fichier statique pose dans local/
105 // en entree : un <head> html.
106 // http://doc.spip.org/@compacte_head_css
107 function compacte_head_css($flux) {
108 $url_base = url_de_base();
109 $url_page = substr(generer_url_public('A'), 0, -1);
110 $dir = preg_quote($url_page,',').'|'.preg_quote(preg_replace(",^$url_base,",_DIR_RACINE,$url_page),',');
111
112 $css = array();
113 $flux_nocomment = preg_replace(",<!--.*-->,Uims","",$flux);
114 foreach (extraire_balises($flux_nocomment, 'link') as $s) {
115 if (extraire_attribut($s, 'rel') === 'stylesheet'
116 AND (!($type = extraire_attribut($s, 'type'))
117 OR $type == 'text/css')
118 AND is_null(extraire_attribut($s, 'name')) # css nommee : pas touche
119 AND is_null(extraire_attribut($s, 'id')) # idem
120 AND !strlen(strip_tags($s))
121 AND $src = preg_replace(",^$url_base,",_DIR_RACINE,extraire_attribut($s, 'href'))
122 AND (
123 // regarder si c'est du format spip.php?page=xxx
124 preg_match(',^('.$dir.')(.*)$,', $src, $r)
125 OR (
126 // ou si c'est un fichier
127 // enlever un timestamp eventuel derriere un nom de fichier statique
128 $src2 = preg_replace(",[.]css[?].+$,",'.css',$src)
129 // verifier qu'il n'y a pas de ../ ni / au debut (securite)
130 AND !preg_match(',(^/|\.\.),', substr($src2,strlen(_DIR_RACINE)))
131 // et si il est lisible
132 AND @is_readable($src2)
133 )
134 )) {
135 $media = strval(extraire_attribut($s, 'media'));
136 if ($media==='') $media='all';
137 if ($r)
138 $css[$media][$s] = explode('&',
139 str_replace('&amp;', '&', $r[2]), 2);
140 else
141 $css[$media][$s] = $src;
142 }
143 }
144
145 // et mettre le tout dans un cache statique
146 foreach($css as $m=>$s){
147 // si plus d'une css pour ce media ou si c'est une css dynamique
148 if (count($s)>1 OR is_array(reset($s))){
149 if (list($src,$comms) = filtre_cache_static($s,'css')){
150 $compacte_ecrire_balise_link = charger_fonction('compacte_ecrire_balise_link','');
151 $s = array_keys($s);
152 $flux = str_replace(reset($s),
153 $comms . $compacte_ecrire_balise_link($src,$m)."\n",
154 $flux);
155 $flux = str_replace($s,"",$flux);
156 }
157 }
158 }
159
160 return $flux;
161 }
162
163
164 // http://doc.spip.org/@filtre_cache_static
165 function filtre_cache_static($scripts,$type='js'){
166 $nom = "";
167 if (!is_array($scripts) && $scripts) $scripts = array($scripts);
168 if (count($scripts)){
169 $dir = sous_repertoire(_DIR_VAR,'cache-'.$type);
170 $nom = $dir . md5(serialize($scripts)) . ".$type";
171 if (
172 $GLOBALS['var_mode']=='recalcul'
173 OR !file_exists($nom)
174 ) {
175 $fichier = "";
176 $comms = array();
177 $total = 0;
178 foreach($scripts as $script){
179 if (!is_array($script)) {
180 // c'est un fichier
181 $comm = $script;
182 // enlever le timestamp si besoin
183 $script = preg_replace(",[?].+$,",'',$script);
184 if ($type=='css'){
185 $fonctions = array('urls_absolues_css');
186 if (isset($GLOBALS['compresseur_filtres_css']) AND is_array($GLOBALS['compresseur_filtres_css']))
187 $fonctions = $GLOBALS['compresseur_filtres_css'] + $fonctions;
188 $script = appliquer_fonctions_css_fichier($fonctions, $script);
189 }
190 lire_fichier($script, $contenu);
191 }
192 else {
193 // c'est un squelette
194 $comm = _SPIP_PAGE . "=$script[0]"
195 . (strlen($script[1])?"($script[1])":'');
196 parse_str($script[1],$contexte);
197 $contenu = recuperer_fond($script[0],$contexte);
198 if ($type=='css'){
199 $fonctions = array('urls_absolues_css');
200 if (isset($GLOBALS['compresseur_filtres_css']) AND is_array($GLOBALS['compresseur_filtres_css']))
201 $fonctions = $GLOBALS['compresseur_filtres_css'] + $fonctions;
202 $contenu = appliquer_fonctions_css_contenu($fonctions, $contenu, self('&'));
203 }
204 }
205 $f = 'compacte_'.$type;
206 $fichier .= "/* $comm */\n". $f($contenu) . "\n\n";
207 $comms[] = $comm;
208 $total += strlen($contenu);
209 }
210
211 // calcul du % de compactage
212 $pc = ($total?intval(1000*strlen($fichier)/$total)/10:0);
213 $comms = "compact [\n\t".join("\n\t", $comms)."\n] $pc%";
214 $fichier = "/* $comms */\n\n".$fichier;
215
216 // ecrire
217 ecrire_fichier($nom,$fichier,true);
218 // ecrire une version .gz pour content-negociation par apache, cf. [11539]
219 ecrire_fichier("$nom.gz",$fichier,true);
220 }
221
222 // closure compiler ou autre super-compresseurs
223 compresse_encore($nom, $type);
224
225 }
226
227 // Le commentaire detaille n'apparait qu'au recalcul, pour debug
228 return array($nom, (isset($comms) AND $comms) ? "<!-- $comms -->\n" : '');
229 }
230
231 // experimenter le Closure Compiler de Google
232 function compresse_encore (&$nom, $type) {
233 # Closure Compiler n'accepte pas des POST plus gros que 200 000 octets
234 # au-dela il faut stocker dans un fichier, et envoyer l'url du fichier
235 # dans code_url ; en localhost ca ne marche evidemment pas
236 if (
237 $GLOBALS['meta']['auto_compress_closure'] == 'oui'
238 AND $type=='js'
239 ) {
240 lire_fichier($nom, $fichier);
241 $dest = dirname($nom).'/'.md5($fichier).'.js';
242 if (!@file_exists($dest)) {
243 include_spip('inc/distant');
244
245 $datas=array(
246 'output_format' => 'text',
247 'output_info' => 'compiled_code',
248 'compilation_level' => 'SIMPLE_OPTIMIZATIONS', // 'SIMPLE_OPTIMIZATIONS', 'WHITESPACE_ONLY', 'ADVANCED_OPTIMIZATIONS'
249 );
250 if (strlen($fichier) < 200000)
251 $datas['js_code'] = $fichier;
252 else
253 $datas['url_code'] = url_absolue($nom);
254
255 $cc = recuperer_page('http://closure-compiler.appspot.com/compile',
256 $trans=false, $get_headers=false,
257 $taille_max = null,
258 $datas,
259 $boundary = -1);
260 if ($cc AND !preg_match(',^\s*Error,', $cc)) {
261 spip_log('Closure Compiler: success');
262 $cc = "/* $nom + Closure Compiler */\n".$cc;
263 ecrire_fichier ($dest, $cc, true);
264 ecrire_fichier ("$dest.gz", $cc, true);
265 } else
266 ecrire_fichier ($dest, '', true);
267 }
268 if (@filesize($dest))
269 $nom = $dest;
270 }
271 }
272
273 function appliquer_fonctions_css_fichier($fonctions,$css) {
274 if (!preg_match(',\.css$,i', $css, $r)) return $css;
275
276 $url_absolue_css = url_absolue($css);
277
278 // verifier qu'on a un array
279 if (is_string($fonctions))
280 $fonctions = array($fonctions);
281
282 $sign = implode(",",$fonctions);
283 $sign = substr(md5("$css-$sign"), 0,8);
284
285 $file = basename($css,'.css');
286 $file = sous_repertoire (_DIR_VAR, 'cache-css')
287 . preg_replace(",(.*?)(_rtl|_ltr)?$,","\\1-f-" . $sign . "\\2",$file)
288 . '.css';
289
290 if ((@filemtime($f) > @filemtime($css))
291 AND ($GLOBALS['var_mode'] != 'recalcul'))
292 return $f;
293
294 if ($url_absolue_css==$css){
295 if (strncmp($GLOBALS['meta']['adresse_site'],$css,$l=strlen($GLOBALS['meta']['adresse_site']))!=0
296 OR !lire_fichier(_DIR_RACINE . substr($css,$l), $contenu)){
297 include_spip('inc/distant');
298 if (!$contenu = recuperer_page($css))
299 return $css;
300 }
301 }
302 elseif (!lire_fichier($css, $contenu))
303 return $css;
304
305 $contenu = appliquer_fonctions_css_contenu($fonctions, $contenu, $css);
306
307 // ecrire la css
308 if (!ecrire_fichier($file, $contenu))
309 return $css;
310
311 return $file;
312 }
313
314 function appliquer_fonctions_css_contenu($fonctions, &$contenu, $base) {
315 foreach($fonctions as $f)
316 if (function_exists($f))
317 $contenu = $f($contenu, $base);
318 return $contenu;
319 }
320
321
322 function compresseur_embarquer_images_css($contenu, $source){
323 #$path = suivre_lien(url_absolue($source),'./');
324 $base = ((substr($source,-1)=='/')?$source:(dirname($source).'/'));
325
326 return preg_replace_callback(
327 ",url\s*\(\s*['\"]?([^'\"/][^:]*[.](png|gif|jpg))['\"]?\s*\),Uims",
328 create_function('$x',
329 'return "url(\"".filtre_embarque_fichier($x[1],"'.$base.'")."\")";'
330 ), $contenu);
331 }