Ignore:
Timestamp:
Jan 7, 2011, 2:52:51 PM (10 years ago)
Author:
cedric@…
Message:

refactoring du compresseur :
Separer par groupes fonctionnel :

  • minifier pour tout ce qui concerne la minification css ou js d'un fichier
  • concatener pour le regroupement de plusieurs fichiers en un (au passage, la fonction est generalisee et les specificites css/js sont simplement gerees par les callbacks passees en appel)
  • embarquer pour tout ce qui concerne(ra) l'inclusion des images en base64 dans les css (a developper)

On rationalise les nommage

File:
1 edited

Legend:

Unmodified
Added
Removed
  • _core_/plugins/compresseur/inc/compresseur.php

    r43372 r43373  
    2828 * @return string
    2929 */
    30 function compacte_ecrire_balise_js_dist(&$flux, $pos, $src, $comments = ""){
     30function compresseur_ecrire_balise_js_dist(&$flux, $pos, $src, $comments = ""){
    3131        $comments .= "<script type='text/javascript' src='$src'></script>";
    3232  $flux = substr_replace($flux,$comments,$pos,0);
     
    4949 * @return string
    5050 */
    51 function compacte_ecrire_balise_css_dist(&$flux, $pos, $src, $comments = "", $media=""){
     51function compresseur_ecrire_balise_css_dist(&$flux, $pos, $src, $comments = "", $media=""){
    5252        $comments .= "<link rel='stylesheet'".($media?" media='$media'":"")." href='$src' type='text/css' />";
    5353  $flux = substr_replace($flux,$comments,$pos,0);
     
    5656
    5757/**
    58  * Minifier un contenu CSS
    59  * Si $options est vide
    60  *      on utilise la methode regexp simple
    61  * Si $options est une chaine non vide
    62  *  elle definit un media a appliquer a la css
    63  *      si la css ne contient aucun @media ni @import, on encapsule tout dans "@media $option {...}" et on utilise regexp
    64  *  sinon on utilise csstidy pour ne pas faire d'erreur, mais c'est 12 fois plus lent
    65  * Si $options sous forme de array()
    66  *      on pass par csstidy pour parser le code
    67  *  et produire un contenu plus compact et prefixe eventuellement par un @media
    68  * options disponibles :
    69  *  string media : media qui seront utilises pour encapsuler par @media
    70  *        les selecteurs sans media
    71  *  string template : format de sortie parmi 'low','default','high','highest'
    72  * @param string $contenu  contenu css
    73  * @param mixed $options options de minification
    74  * @return string
    75  */
    76 function compacte_css ($contenu, $options='') {
    77         if (is_string($options) AND $options){
    78                 if ($options=="all") // facile : media all => ne rien preciser
    79                         $options = "";
    80                 elseif (
    81                                         strpos($contenu,"@media")==false
    82                         AND strpos($contenu,"@import")==false
    83                         ){
    84                         $contenu = "@media $options {\n$contenu\n}\n";
    85                         $options="";
    86                 }
    87                 else
    88                         $options = array('media'=>$options);
    89         }
    90         if (!is_array($options)){
    91 
    92                 // nettoyer la css de tout ce qui sert pas
    93                 // pas de commentaires
    94                 $contenu = preg_replace(",/\*.*\*/,Ums","",$contenu);
    95                 $contenu = preg_replace(",\s//[^\n]*\n,Ums","",$contenu);
    96                 // espaces autour des retour lignes
    97                 $contenu = str_replace("\r\n","\n",$contenu);
    98                 $contenu = preg_replace(",\s+\n,ms","\n",$contenu);
    99                 $contenu = preg_replace(",\n\s+,ms","\n",$contenu);
    100                 // pas d'espaces consecutifs
    101                 $contenu = preg_replace(",\s(?=\s),Ums","",$contenu);
    102                 // pas d'espaces avant et apres { ; ,
    103                 $contenu = preg_replace("/\s?({|;|,)\s?/ms","$1",$contenu);
    104                 // supprimer les espaces devant : sauf si suivi d'une lettre (:after, :first...)
    105                 $contenu = preg_replace("/\s:([^a-z])/ims",":$1",$contenu);
    106                 // supprimer les espaces apres :
    107                 $contenu = preg_replace("/:\s/ms",":",$contenu);
    108                 // pas d'espaces devant }
    109                 $contenu = preg_replace("/\s}/ms","}",$contenu);
    110 
    111                 // ni de point virgule sur la derniere declaration
    112                 $contenu = preg_replace("/;}/ms","}",$contenu);
    113                 // pas d'espace avant !important
    114                 $contenu = preg_replace("/\s!\s?important/ms","!important",$contenu);
    115                 // passser les codes couleurs en 3 car si possible
    116                 // uniquement si non precedees d'un [="'] ce qui indique qu'on est dans un filter(xx=#?...)
    117                 $contenu = preg_replace(";([:\s,(])#([0-9a-f])(\\2)([0-9a-f])(\\4)([0-9a-f])(\\6)(?=[^\w\-]);i","$1#$2$4$6",$contenu);
    118                 // remplacer font-weight:bold par font-weight:700
    119                 $contenu = preg_replace("/font-weight:bold/ims","font-weight:700",$contenu);
    120                 // remplacer font-weight:normal par font-weight:400
    121                 $contenu = preg_replace("/font-weight:normal/ims","font-weight:400",$contenu);
    122 
    123                 // enlever le 0 des unites decimales
    124                 $contenu = preg_replace("/0[.]([0-9]+em)/ims",".$1",$contenu);
    125                 // supprimer les declarations vides
    126                 $contenu = preg_replace(",\s([^{}]*){},Ums"," ",$contenu);
    127                 // zero est zero, quelle que soit l'unite
    128                 $contenu = preg_replace("/([^0-9.]0)(em|px|pt|%)/ms","$1",$contenu);
    129 
    130                 // renommer les couleurs par leurs versions courtes quand c'est possible
    131                 $colors = array(
    132                         'source'=>array('black','fuchsia','white','yellow','#800000','#ffa500','#808000','#800080','#008000','#000080','#008080','#c0c0c0','#808080','#f00'),
    133                         'replace'=>array('#000' ,'#F0F'   ,'#FFF' ,'#FF0'  ,'maroon' ,'orange' ,'olive'  ,'purple' ,'green'  ,'navy'   ,'teal'   ,'silver' ,'gray'   ,'red')
    134                 );
    135                 foreach($colors['source'] as $k=>$v){
    136                         $colors['source'][$k]=";([:\s,(])".$v."(?=[^\w\-]);ms";
    137                         $colors['replace'][$k] = "$1".$colors['replace'][$k];
    138                 }
    139                 $contenu = preg_replace($colors['source'],$colors['replace'],$contenu);
    140 
    141                 // raccourcir les padding qui le peuvent (sur 3 ou 2 valeurs)
    142                 $contenu = preg_replace(",padding:([^\s;}]+)\s([^\s;}]+)\s([^\s;}]+)\s(\\2),ims","padding:$1 $2 $3",$contenu);
    143                 $contenu = preg_replace(",padding:([^\s;}]+)\s([^\s;}]+)\s(\\1)([;}!]),ims","padding:$1 $2$4",$contenu);
    144 
    145                 // raccourcir les margin qui le peuvent (sur 3 ou 2 valeurs)
    146                 $contenu = preg_replace(",margin:([^\s;}]+)\s([^\s;}]+)\s([^\s;}]+)\s(\\2),ims","margin:$1 $2 $3",$contenu);
    147                 $contenu = preg_replace(",margin:([^\s;}]+)\s([^\s;}]+)\s(\\1)([;}!]),ims","margin:$1 $2$4",$contenu);
    148 
    149                 $contenu = trim($contenu);
    150 
    151                 return $contenu;
    152         }
    153         else {
    154                 // compression avancee en utilisant csstidy
    155                 // beaucoup plus lent, mais necessaire pour placer des @media la ou il faut
    156                 // si il y a deja des @media ou des @import
    157 
    158                 // modele de sortie plus ou moins compact
    159                 $template = 'high';
    160                 if (isset($options['template']) AND in_array($options['template'],array('low','default','high','highest')))
    161                         $template = $options['template'];
    162                 // @media eventuel pour prefixe toutes les css
    163                 // et regrouper plusieurs css entre elles
    164                 $media = "";
    165                 if (isset($options['media']))
    166                         $media = "@media ".$options['media']." ";
    167 
    168                 include_spip("lib/csstidy/class.csstidy");
    169                 $css = new csstidy();
    170 
    171                 // essayer d'optimiser les font, margin, padding avec des ecritures raccourcies
    172                 $css->set_cfg('optimise_shorthands',2);
    173                 $css->set_cfg('template',$template);
    174                 $css->parse($contenu);
    175                 return $css->print->plain($media);
    176         }
    177 }
    178 
    179 /**
    18058 * Extraire les balises CSS a compacter et retourner un tableau
    18159 * balise => src
    182  * 
     60 *
    18361 * @param  $flux
    18462 * @param  $url_base
    18563 * @return array
    18664 */
    187 function compacte_js($flux) {
    188         if (!strlen($flux))
    189                 return $flux;
    190 
    191         include_spip('lib/JavascriptPacker/class.JavaScriptPacker');
    192         $packer = new JavaScriptPacker($flux, 0, true, false);
    193 
    194         // en cas d'echec (?) renvoyer l'original
    195         if (!strlen($t = $packer->pack())) {
    196                 spip_log('erreur de compacte_js');
    197                 return $flux;
    198         }
    199         return $t;
    200 }
    201 
    202 /**
    203  * Compacter du javascript plus intensivement
    204  * grace au google closure compiler
    205  *
    206  * @param string $content
    207  * @param bool $file
    208  * @return string
    209  */
    210 function compacte_js_more($content,$file=false) {
    211         # Closure Compiler n'accepte pas des POST plus gros que 200 000 octets
    212         # au-dela il faut stocker dans un fichier, et envoyer l'url du fichier
    213         # dans code_url ; en localhost ca ne marche evidemment pas
    214         if ($file) {
    215                 $nom = $content;
    216                 lire_fichier($nom, $content);
    217                 $dest = dirname($nom).'/'.md5($content).'.js';
    218                 if (file_exists($dest))
    219                         if (filesize($dest))
    220                                 return $dest;
    221                         else
    222                                 return $nom;
    223         }
    224 
    225         if (!$file AND strlen($content)>200000)
    226                 return $content;
    227 
    228         include_spip('inc/distant');
    229 
    230         $datas=array(
    231                 'output_format' => 'text',
    232                 'output_info' => 'compiled_code',
    233                 'compilation_level' => 'SIMPLE_OPTIMIZATIONS', // 'SIMPLE_OPTIMIZATIONS', 'WHITESPACE_ONLY', 'ADVANCED_OPTIMIZATIONS'
    234         );
    235         if (!$file OR strlen($content) < 200000)
    236                 $datas['js_code'] = $content;
    237         else
    238                 $datas['url_code'] = url_absolue($nom);
    239 
    240         $cc = recuperer_page('http://closure-compiler.appspot.com/compile',
    241                 $trans=false, $get_headers=false,
    242                 $taille_max = null,
    243                 $datas,
    244                 $boundary = -1);
    245 
    246         if ($cc AND !preg_match(',^\s*Error,', $cc)) {
    247                 spip_log('Closure Compiler: success');
    248                 $cc = "/* $nom + Closure Compiler */\n".$cc;
    249                 if ($file){
    250                         ecrire_fichier ($dest, $cc, true);
    251                         ecrire_fichier ("$dest.gz", $cc, true);
    252                         $content = $dest;
    253                 }
    254                 else
    255                         $content = &$cc;
    256         } else {
    257                 if ($file)
    258                         ecrire_fichier ($dest, '', true);
    259         }
    260         return $content;
    261 }
    262 
    263 
    264 /**
    265  * Extraire les balises CSS a compacter et retourner un tableau
    266  * balise => src
    267  *
    268  * @param  $flux
    269  * @param  $url_base
    270  * @return array
    271  */
    272 function extraire_balises_css_dist($flux, $url_base){
     65function compresseur_extraire_balises_css_dist($flux, $url_base){
    27366        $balises = extraire_balises($flux,'link');
    27467        $files = array();
     
    29386 * @return array
    29487 */
    295 function extraire_balises_js_dist($flux, $url_base){
     88function compresseur_extraire_balises_js_dist($flux, $url_base){
    29689        $balises = extraire_balises($flux,'script');
    29790        $files = array();
     
    321114        $dir = preg_quote($url_page,',').'|'.preg_quote(preg_replace(",^$url_base,",_DIR_RACINE,$url_page),',');
    322115
    323         if (!$extraire_balises = charger_fonction("extraire_balises_$format",'',true))
     116        if (!$extraire_balises = charger_fonction("compresseur_extraire_balises_$format",'',true))
    324117                return $flux;
    325118
     
    347140        }
    348141
    349         if (list($src,$comms) = filtre_cache_static($files,$format)){
    350                 $compacte_ecrire_balise = charger_fonction("compacte_ecrire_balise_$format",'');
     142        $callbacks = array('each_min'=>'callback_minifier_'.$format.'_file', 'all_min'=>'callback_minifier_encore');
     143        if ($format=="css")
     144                $callbacks['each_pre'] = 'compresseur_callback_prepare_css';
     145
     146        include_spip('inc/compresseur_concatener');
     147        include_spip('inc/compresseur_minifier');
     148        if (list($src,$comms) = concatener_fichiers($files, $format, $callbacks)){
     149                $compacte_ecrire_balise = charger_fonction("compresseur_ecrire_balise_$format",'');
    351150                $files = array_keys($files);
    352151                // retrouver la position du premier fichier compacte
     
    362161
    363162
    364 // http://doc.spip.org/@filtre_cache_static
    365 /**
    366  * Retrouve ou genere le fichier statique correspondant a une liste de fichiers
    367  * fournis en arguments
    368  * @param  $files
    369  * @param string $format
     163/**
     164 * lister les fonctions de preparation des feuilles css
     165 * avant minification
     166 *
    370167 * @return array
    371168 */
    372 function filtre_cache_static($files,$format='js'){
    373         $nom = "";
    374         if (!is_array($files) && $files) $files = array($files);
    375         if (count($files)){
    376                 $minifier = 'compacte_'.$format;
    377          
    378                 // on trie la liste de files pour calculer le nom
    379                 // necessaire pour retomber sur le meme fichier
    380                 // si on renome une url a la volee pour enlever le var_mode=recalcul
    381                 // mais attention, il faut garder l'ordre initial pour la minification elle meme !
    382                 $s2 = $files;
    383                 ksort($s2);
    384                 $dir = sous_repertoire(_DIR_VAR,'cache-'.$format);
    385                 $nom = $dir . md5(serialize($s2)) . ".$format";
    386                 if (
    387                         $GLOBALS['var_mode']=='recalcul'
    388                         OR !file_exists($nom)
    389                 ) {
    390                         $fichier = "";
    391                         $comms = array();
    392                         $total = 0;
    393                         $s2 = false;
    394                         foreach($files as $key=>$file){
    395                                 if (!is_array($file)) {
    396                                         // c'est un fichier
    397                                         $comm = $file;
    398                                         // enlever le timestamp si besoin
    399                                         $file = preg_replace(",[?].+$,",'',$file);
    400                                         if ($format=='css'){
    401                                                 $fonctions = array('urls_absolues_css');
    402                                                 if (isset($GLOBALS['compresseur_filtres_css']) AND is_array($GLOBALS['compresseur_filtres_css']))
    403                                                         $fonctions = $GLOBALS['compresseur_filtres_css'] + $fonctions;
    404                                                 $file = appliquer_fonctions_css_fichier($fonctions, $file);
    405                                         }
    406                                         lire_fichier($file, $contenu);
    407                                 }
    408                                 else {
    409                                         // c'est un squelette
    410                                         $comm = _SPIP_PAGE . "=$file[0]"
    411                                                 . (strlen($file[1])?"($file[1])":'');
    412                                         parse_str($file[1],$contexte);
    413                                         $contenu = recuperer_fond($file[0],$contexte);
    414                                         if ($format=='css'){
    415                                                 $fonctions = array('urls_absolues_css');
    416                                                 if (isset($GLOBALS['compresseur_filtres_css']) AND is_array($GLOBALS['compresseur_filtres_css']))
    417                                                         $fonctions = $GLOBALS['compresseur_filtres_css'] + $fonctions;
    418                                                 $contenu = appliquer_fonctions_css_contenu($fonctions, $contenu, self('&'));
    419                                         }
    420                                         // enlever le var_mode si present pour retrouver la css minifiee standard
    421                                         if (strpos($file[1],'var_mode')!==false) {
    422                                                 if (!$s2) $s2 = $files;
    423                                                 unset($s2[$key]);
    424                                                 $key = preg_replace(',(&(amp;)?)?var_mode=[^&\'"]*,','',$key);
    425                                                 $file[1] = preg_replace(',&?var_mode=[^&\'"]*,','',$file[1]);
    426                                                 $s2[$key] = $file;
    427                                         }
    428                                 }
    429                                 // minifier en passant le media en option si c'est une css
    430                                 // (ignore pour les js)
    431                                 $fichier .= "/* $comm */\n". $minifier($contenu, extraire_attribut($key,'media')) . "\n\n";
    432                                 $comms[] = $comm;
    433                                 $total += strlen($contenu);
    434                         }
    435 
    436                         // calcul du % de compactage
    437                         $pc = intval(1000*strlen($fichier)/$total)/10;
    438                         $comms = "compact [\n\t".join("\n\t", $comms)."\n] $pc%";
    439                         $fichier = "/* $comms */\n\n".$fichier;
    440 
    441                         if ($s2) {
    442                                 ksort($s2);
    443                                 $nom = $dir . md5(serialize($s2)) . ".$format";
    444                         }
    445 
    446                         // ecrire
    447                         ecrire_fichier($nom,$fichier,true);
    448                         // ecrire une version .gz pour content-negociation par apache, cf. [11539]
    449                         ecrire_fichier("$nom.gz",$fichier,true);
    450                         // closure compiler ou autre super-compresseurs
    451                         // a appliquer sur le fichier final
    452                         $nom = compresse_encore($nom, $format);
    453                 }
    454 
    455 
    456         }
    457 
    458         // Le commentaire detaille n'apparait qu'au recalcul, pour debug
    459         return array($nom, (isset($comms) AND $comms) ? "<!-- $comms -->\n" : '');
    460 }
    461 
    462 /**
    463  * Minification additionnelle :
    464  * experimenter le Closure Compiler de Google
    465  * @param string $nom
    466  *   nom d'un fichier a minifier encore plus
    467  * @param string $format
    468  *   format css ou js
    469  * @return string
    470  */
    471 function compresse_encore (&$nom, $format) {
    472         # Closure Compiler n'accepte pas des POST plus gros que 200 000 octets
    473         # au-dela il faut stocker dans un fichier, et envoyer l'url du fichier
    474         # dans code_url ; en localhost ca ne marche evidemment pas
    475         if (
    476         $GLOBALS['meta']['auto_compress_closure'] == 'oui'
    477         AND $format=='js'
    478         ) {
    479                 $nom = compacte_js_more($nom,true);
    480         }
    481         return $nom;
    482 }
    483 
    484 function appliquer_fonctions_css_fichier($fonctions,$css) {
     169function compresseur_liste_fonctions_prepare_css(){
     170        static $fonctions = null;
     171
     172        if (is_null($fonctions)){
     173                $fonctions = array('urls_absolues_css');
     174                // les fonctions de preparation aux CSS peuvent etre personalisees
     175                // via la globale $compresseur_filtres_css sous forme de tableau de fonctions ordonnees
     176                if (isset($GLOBALS['compresseur_filtres_css']) AND is_array($GLOBALS['compresseur_filtres_css']))
     177                        $fonctions = $GLOBALS['compresseur_filtres_css'] + $fonctions;
     178        }
     179  return $fonctions;
     180}
     181
     182
     183/**
     184 * Preparer un fichier CSS avant sa minification
     185 * @param string $css
     186 * @param bool|string $is_inline
     187 * @return bool|int|null|string
     188 */
     189function &compresseur_callback_prepare_css(&$css, $is_inline = false, $fonctions=null) {
     190        if ($is_inline) return compresseur_callback_prepare_css_inline($css,$is_inline);
    485191        if (!preg_match(',\.css$,i', $css, $r)) return $css;
    486192
    487193        $url_absolue_css = url_absolue($css);
    488194
    489         // verifier qu'on a un array
    490         if (is_string($fonctions))
    491                 $fonctions = array($fonctions);
     195        if (!$fonctions) $fonctions = compresseur_liste_fonctions_prepare_css();
     196        elseif (is_string($fonctions)) $fonctions = array($fonctions);
    492197
    493198        $sign = implode(",",$fonctions);
     
    499204                . '.css';
    500205
    501         if ((@filemtime($f) > @filemtime($css))
    502         AND ($GLOBALS['var_mode'] != 'recalcul'))
    503                 return $f;
     206        if ((@filemtime($file) > @filemtime($css))
     207                AND ($GLOBALS['var_mode'] != 'recalcul'))
     208                return $file;
    504209
    505210        if ($url_absolue_css==$css){
     
    514219                return $css;
    515220
    516         $contenu = appliquer_fonctions_css_contenu($fonctions, $contenu, $css);
     221        $contenu = compresseur_callback_prepare_css_inline($contenu, $css, $fonctions);
    517222
    518223        // ecrire la css
     
    523228}
    524229
    525 function appliquer_fonctions_css_contenu($fonctions, &$contenu, $base) {
     230/**
     231 * Preparer du contenu CSS inline avant minification
     232 *
     233 * @param string $contenu
     234 * @param string $url_base
     235 * @return string
     236 */
     237function &compresseur_callback_prepare_css_inline(&$contenu, $url_base, $fonctions=null) {
     238        if (!$fonctions) $fonctions = compresseur_liste_fonctions_prepare_css();
     239        elseif (is_string($fonctions)) $fonctions = array($fonctions);
     240
    526241        foreach($fonctions as $f)
    527242                if (function_exists($f))
    528                         $contenu = $f($contenu, $base);
     243                        $contenu = $f($contenu, $url_base);
     244       
    529245        return $contenu;
    530246}
    531247
    532 
    533 function compresseur_embarquer_images_css($contenu, $source){
    534         #$path = suivre_lien(url_absolue($source),'./');
    535         $base = ((substr($source,-1)=='/')?$source:(dirname($source).'/'));
    536 
    537         return preg_replace_callback(
    538                 ",url\s*\(\s*['\"]?([^'\"/][^:]*[.](png|gif|jpg))['\"]?\s*\),Uims",
    539                 create_function('$x',
    540                         'return "url(\"".filtre_embarque_fichier($x[1],"'.$base.'")."\")";'
    541                 ), $contenu);
    542 }
Note: See TracChangeset for help on using the changeset viewer.