source: spip-zone/_plugins_/less-css/trunk/lesscss_fonctions.php @ 66259

Last change on this file since 66259 was 66259, checked in by cedric@…, 7 years ago

Centraliser dans ce plugin la prise en charge des .less dans la balise #CSS :

  • Prise en charge de la balise #CSS{style.css}
  • Regles :
  • - cherche un .css ou un .css.html ou un .less comme feuille de style
  • - si un seul des 3 trouve dans le chemin il est renvoye (et compile au passage si .less)
  • - si un .css.html et un .css trouves dans le chemin, c'est le .css.html qui est pris (surcharge d'un statique avec une css calculee)
  • - si un .less et un (.css ou .css.html) on compare la priorite du chemin des deux trouves :
  • le plus prioritaire des 2 est choisi
  • si priorite equivalente on choisi le (.css ou .css.html) qui est le moins couteux a produire
  • permet d'avoir dans le meme dossier le .less et sa version compilee .css : cette derniere est utilisee
  • #CSS{style.css} renvoie dans tous les cas un fichier .css qui est soit :
  • - un .less compile en .css
  • - un .css statique
  • - un .css.html calcule en .css

+ prefixage fonctions et PHPDoc

  • Property svn:executable set to *
File size: 8.6 KB
Line 
1<?php
2/*
3 * Plugin LessCSS
4 * Distribue sous licence MIT
5 *
6 */
7
8if (!defined("_ECRIRE_INC_VERSION")) return;
9
10/**
11 * Compiler des styles inline LESS en CSS
12 *
13 * @param string $style
14 *   contenu du .less
15 * @param array $contexte
16 *   file : chemin du fichier compile
17 *          utilise en cas de message d'erreur, et pour le repertoire de reference des @import
18 * @return string
19 */
20function lesscss_compile($style, $contexte = array()){
21        require_once 'lessphp/lessc.inc.php';
22
23        $import_dir = null;
24        if (isset($contexte['file']))
25                $import_dir = dirname($contexte['file']);
26
27        // le compilateur lessc compile le contenu
28        $less = new lessc();
29        if ($import_dir){
30                $import_dir = rtrim($import_dir,"/")."/";
31                $less->importDir = $import_dir;
32        }
33        try {
34                // avant compilation par less, remplacer les @import_spip par un @import+find_in_path
35                if (preg_match_all(",@import_spip\s+['\"]([^'\"]+)['\"],",$style,$matches,PREG_SET_ORDER)){
36                        if ($import_dir){
37                                // faker le chemin depuis le $import_dir
38                                $i = $import_dir."dummy";$base = "";
39                                while ($i AND ($i = dirname($i))!=".") {$base.="../";};
40                                $ide= explode('/',$import_dir);
41                                foreach($matches as $m){
42                                        if ($f=find_in_path($m[1])){
43                                                // calculer un chemin relatif propre avec le moins de ../ possible
44                                                $fe = explode("/",$f);
45                                                $be = explode("/",$base);
46                                                $l = min(count($ide),count($fe));
47                                                for($i=0;$i<$l;$i++){
48                                                        if (reset($fe)==$ide[$i]){
49                                                                array_shift($fe);
50                                                                array_shift($be);
51                                                        }
52                                                        else
53                                                                break;
54                                                }
55                                                $be = implode("/",$be);
56                                                $fe = implode("/",$fe);
57                                                $style = str_replace($m[0],"@import '$be$fe'",$style);
58                                        }
59                                }
60                        }
61                        else {
62                                spip_log('lessc @import_spip sans contexte de chemin','less'._LOG_ERREUR);
63                                erreur_squelette(
64                                        "LESS : @import_spip sans contexte de chemin"
65                                        . (isset($contexte['file'])?" fichier ".$contexte['file']:"")
66                                );
67                        }
68                }
69                $out = $less->parse($style);
70                return $out;
71        }
72        // en cas d'erreur, on retourne du vide...
73        catch (exception $ex) {
74                spip_log('lessc fatal error:'.$ex->getMessage(),'less'._LOG_ERREUR);
75                erreur_squelette(
76                        "LESS : Echec compilation"
77                        . (isset($contexte['file'])?" fichier ".$contexte['file']:"")
78                  . "<br />".$ex->getMessage()
79                );
80                return '';
81        }
82}
83
84/**
85 * Transformer du LESS en CSS
86 * Peut prendre en entree
87 * - un fichier .css ou .less :
88 *   [(#CHEMIN{messtyles.less.css}|less_css)]
89 *   la sortie est un chemin vers un fichier CSS
90 * - des styles inline,
91 *   pour appliquer dans une feulle less calculee :
92 *   #FILTRE{less_css}
93 *   la sortie est du style inline
94 *
95 * @param string $source
96 * @return string
97 */
98function less_css($source){
99        // Si on n'importe pas, est-ce un fichier ?
100        if (!preg_match(',[\s{}],', $source)
101          AND preg_match(',\.(less|css)$,i', $source, $r)
102          AND file_exists($source)) {
103                static $done = array();
104                // ne pas essayer de compiler deux fois le meme fichier dans le meme hit
105                // si on a echoue une fois, on echouera pareil
106                if (isset($done[$source])) return $done[$source];
107
108                $f = basename($source,$r[0]);
109                $f = sous_repertoire (_DIR_VAR, 'cache-less')
110                . preg_replace(",(.*?)(_rtl|_ltr)?$,","\\1-cssify-"
111                . substr(md5("$source-lesscss"), 0,4) . "\\2", $f, 1)
112                . '.css';
113
114                # si la feuille compilee est plus recente que la feuille source
115                # l'utiliser sans rien faire, sauf si recalcul explicite
116                if ((@filemtime($f) > @filemtime($source))
117                  AND (!defined('_VAR_MODE') OR _VAR_MODE != 'recalcul'))
118                        return $f;
119
120                if (!lire_fichier($source, $contenu))
121                        return $source;
122
123                # compiler le LESS si besoin (ne pas generer une erreur si source vide
124                if (!$contenu){
125                        $contenu = "/* Source $source : vide */\n";
126                }
127                else {
128                        $contenu = lesscss_compile($contenu, array('file'=>$source));
129                }
130                // si erreur de compilation on renvoit un commentaire, et il y a deja eu un log
131                if (!$contenu){
132                        $contenu = "/* Compilation $source : vide */\n";
133                }
134
135                # passer la css en url absolue (on ne peut pas le faire avant, car c'est du LESS, pas des CSS)
136                $contenu = urls_absolues_css($contenu, $source);
137
138                // ecrire le fichier destination, en cas d'echec renvoyer la source
139                if (ecrire_fichier($f, $contenu, true))
140                        return $done[$source] = $f;
141                else
142                        return $done[$source] = $source;
143        }
144        $source = lesscss_compile($source);
145        if (!$source)
146                return "/* Erreur compilation LESS : cf less.log */";
147        else
148                return $source;
149}
150
151
152/**
153 * injecter l'appel au compresseur sous la forme de filtre
154 * pour intervenir sur l'ensemble du head
155 * du squelette public
156 *
157 * @param string $flux
158 * @return string
159 */
160function lesscss_insert_head($flux){
161        $flux .= '<'
162                .'?php header("X-Spip-Filtre: '
163                .'lesscss_cssify_head'
164                .'"); ?'.'>';
165        return $flux;
166}
167
168
169/**
170 * Attraper automatiquement toutes les .less ou .less.css du head
171 * les compiler, et les remplacer par leur css compilee
172 *
173 * @param string $head
174 * @return void
175 */
176function lesscss_cssify_head($head){
177        $url_base = url_de_base();
178        $balises = extraire_balises($head,'link');
179        $files = array();
180        foreach ($balises as $s){
181                if (extraire_attribut($s, 'rel') === 'stylesheet'
182                        AND (!($type = extraire_attribut($s, 'type')) OR $type == 'text/css')
183                        AND $src = extraire_attribut($s, 'href')
184                        // format .less.css ou .less avec un eventuel timestamp ?123456
185                        AND preg_match(",\.(less\.css|less)(\?\d+)?$,",$src)
186                        AND $src = preg_replace(",\?\d+$,","",$src)
187                        AND $src = preg_replace(",^$url_base,",_DIR_RACINE,$src)
188                        AND file_exists($src))
189                        $files[$s] = $src;
190        }
191
192        if (!count($files))
193                return $head;
194
195        foreach($files as $s=>$lessfile){
196                $cssfile = less_css($lessfile);
197                $m = @filemtime($cssfile);
198                $s2 = inserer_attribut($s,"href","$cssfile?$m");
199                $head = str_replace($s, $s2, $head);
200        }
201       
202        return $head;
203}
204
205/*
206 * Prise en charge de la balise #CSS{style.css}
207 * Regles :
208 * - cherche un .css ou un .css.html ou un .less comme feuille de style
209 * - si un seul des 3 trouve dans le chemin il est renvoye (et compile au passage si .less)
210 * - si un .css.html et un .css trouves dans le chemin, c'est le .css.html qui est pris (surcharge d'un statique avec une css calculee)
211 * - si un .less et un (.css ou .css.html) on compare la priorite du chemin des deux trouves :
212 *   le plus prioritaire des 2 est choisi
213 *   si priorite equivalente on choisi le (.css ou .css.html) qui est le moins couteux a produire
214 *   permet d'avoir dans le meme dossier le .less et sa version compilee .css : cette derniere est utilisee
215 *
216 * #CSS{style.css} renvoie dans tous les cas un fichier .css qui est soit :
217 * - un .less compile en .css
218 * - un .css statique
219 * - un .css.html calcule en .css
220 */
221function balise_CSS($p) {
222        $_css = interprete_argument_balise(1,$p);
223        $p->code = "timestamp(direction_css(lesscss_select_css($_css)))";
224        $p->interdire_scripts = false;
225        return $p;
226}
227
228/**
229 * Selectionner de preference la feuille .less (en la compilant)
230 * et sinon garder la .css classiquement
231 *
232 * @param string $css_file
233 * @return string
234 */
235function lesscss_select_css($css_file){
236        if (function_exists('less_css')
237          AND substr($css_file,-4)==".css"){
238                $less_file = substr($css_file,0,-4).".less";
239                $less_or_css = lesscss_find_less_or_css_in_path($less_file, $css_file);
240                if (substr($less_or_css,-5)==".less")
241                        return less_css($less_or_css);
242                else
243                        return $less_or_css;
244        }
245        return find_in_path($css_file);
246}
247
248/**
249 * Faire un find_in_path en cherchant un fichier .less ou .css
250 * et en prenant le plus prioritaire des deux
251 * ce qui permet de surcharger un .css avec un .less ou le contraire
252 * Si ils sont dans le meme repertoire, c'est le .css qui est prioritaire,
253 * par soucis de rapidite
254 *
255 * @param string $less_file
256 * @param string $css_file
257 * @return string
258 */
259function lesscss_find_less_or_css_in_path($less_file, $css_file){
260        $l = find_in_path($less_file);
261        $c = $f = trouver_fond($css_file);
262        if (!$c)
263                $c = find_in_path($css_file);
264
265        if (!$l)
266                return ($f?produire_fond_statique($css_file,array('format'=>'css')):$c);
267        elseif(!$c)
268                return $l;
269
270        // on a un less et un css en concurence
271        // prioriser en fonction de leur position dans le path
272        $path = creer_chemin();
273        foreach($path as $dir) {
274                // css prioritaire
275                if (strncmp($c,$dir . $css_file,strlen($dir . $css_file))==0)
276                        return ($f?produire_fond_statique($css_file,array('format'=>'css')):$c);;
277                if ($l == $dir . $less_file)
278                        return $l;
279        }
280        // on ne doit jamais arriver la !
281        spip_log('Resolution chemin less/css impossible',_LOG_CRITIQUE);
282        debug_print_backtrace();
283        die('Erreur fatale, je suis perdu');
284}
285
286?>
Note: See TracBrowser for help on using the repository browser.