source: spip-zone/_plugins_/bootstrap/trunk/bootstrap2spip/prefixer_css.php @ 115121

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

Un auto-prefixer PHP pour Bootstrap : traite tout ce dont on a effectivement besoin de prefixer dans BS4 a l'exception de

  • selecteur (concerne ::placeholder qui est prefixe en -ms -moz et -webkit dans BS4 dist css)
  • @keyframe (prefixe en -webkit dans BS4 dist css)

cela fait donc suffisament bien le job pour s'en contenter (si on aime la perfection on utilisera prefixfree.js)

NDLR : Je n'ai pas fait un plugin independant car cela ne pretend pas etre exhaustif ni complet, juste suffisant pour traiter BS4

File size: 7.7 KB
Line 
1<?php
2
3/**
4 * Prefixer du css pour ameliorer la compat
5 * Peut prendre en entree
6 * - un fichier .css:
7 *   la sortie est un chemin vers un fichier CSS
8 * - des styles inline,
9 *   pour appliquer dans une feulle scss calculee :
10 *   #FILTRE{prefixer_css}
11 *   la sortie est du style inline
12 *
13 * @param string $source
14 * @return string
15 */
16function prefixer_css($source) {
17        static $chemin = null;
18
19        // Si on n'importe pas, est-ce un fichier ?
20        if (
21                !preg_match(',[\s{}],', $source)
22                and preg_match(',\.css$,i', $source, $r)
23                and file_exists($source)
24        ) {
25
26                $f = basename($source, $r[0]);
27                $f = sous_repertoire(_DIR_VAR, 'cache-prefixer')
28                . preg_replace(
29                        ',(.*?)(_rtl|_ltr)?$,',
30                        "\\1-prefixer-" . substr(md5("$source-prefixer"), 0, 7) . "\\2",
31                        $f,
32                        1
33                )
34                . '.css';
35
36                # si la feuille prefixee est plus recente que la feuille source
37                # l'utiliser sans rien faire, sauf si il y a un var_mode
38                $changed = false;
39                if (@filemtime($f) < @filemtime($source)){
40                        $changed = true;
41                }
42
43                // si pas change ET pas de var_mode du tout, rien a faire (performance)
44                if (!$changed
45                        AND !defined('_VAR_MODE'))
46                        return $f;
47
48                $contenu = false;
49                if (!lire_fichier($source, $contenu)) {
50                        return $source;
51                }
52
53                // prefixer le CSS
54                $contenu = prefixer_css_inline($contenu);
55
56                // passer la css en url absolue
57                $contenu = urls_absolues_css($contenu, $source);
58
59                // ecrire le fichier destination, en cas d'echec renvoyer la source
60                // on ecrit sur un fichier
61                if (ecrire_fichier($f.'.last', $contenu, true)) {
62                        if ($changed or md5_file($f) != md5_file($f.'.last')) {
63                                @copy($f.'.last', $f);
64                                // eviter que PHP ne reserve le vieux timestamp
65                                if (version_compare(PHP_VERSION, '5.3.0') >= 0) {
66                                        clearstatcache(true, $f);
67                                } else {
68                                        clearstatcache();
69                                }
70                        }
71
72                        return $f;
73                } else {
74                        return $source;
75                }
76        }
77
78        $source = prefixer_css_inline($source);
79        return $source;
80}
81
82function prefixer_css_inline($css) {
83
84        // les proprietes qu'on veut prefixer par une ou plusieurs variantes
85        $list = [
86                "align-items" => ["-ms-flex-align"],
87                "align-content" => ["-ms-flex-line-pack"],
88                "align-self" => ["-ms-flex-item-align"],
89                "animation" => ["-webkit-animation"],
90                "appearance" => ["-webkit-appearance", "-moz-appearance"],
91
92                "backdrop-filter" => ["-webkit-backdrop-filter"],
93                "backface-visibility" => ["-webkit-backface-visibility"],
94
95                "column-count" => ["-webkit-column-count", "-moz-column-count"],
96    "column-gap" => ["-webkit-column-gap", "-moz-column-gap"],
97
98                "flex-basis" => ["-ms-flex-preferred-size"],
99                "flex-grow" => ["-ms-flex-positive"],
100                "flex-wrap" => ["-ms-flex-wrap"],
101                "flex-flow" => ["-ms-flex-flow"],
102                "flex-shrink" => ["-ms-flex-negative"],
103                "flex-direction" => ["-ms-flex-direction"],
104                "flex" => ["-ms-flex"],
105
106                "justify-content" => ["-ms-flex-pack"],
107
108                "order" => ["-ms-flex-order"],
109
110                "user-select" => ["-webkit-user-select", "-moz-user-select", "-ms-user-select"],
111
112                "text-decoration" => ["-webkit-text-decoration"],
113    "text-decoration-skip-ink" => ["-webkit-text-decoration-skip-ink"],
114                "touch-action" => ["-ms-touch-action"],
115    "transform" => ["-webkit-transform"],
116
117                // cas particuliers : la propriete ne change pas, mais on passe par le remplacement de valeurs qui fait le job de prefixage
118                "display" => ["display"],
119                "position" => ["position"],
120        ];
121
122        // les renommages de valeur sur les proprietes remplacees
123        $values_replacement = [
124                "-ms-flex-align" => [
125                        "flex-start" => "start",
126                        "flex-end" => "end",
127                ],
128                "-ms-flex-item-align" => [
129                        "flex-start" => "start",
130                        "flex-end" => "end",
131                ],
132                "-ms-flex-line-pack" => [
133                        "flex-start" => "start",
134                        "flex-end" => "end",
135                        "space-between" => "justify",
136                        "space-around" => "distribute",
137                ],
138                "-ms-flex-pack" => [
139                        "flex-start" => "start",
140                        "flex-end" => "end",
141                        "space-between" => "justify",
142                        "space-around" => "distribute",
143                ],
144                "display" => [
145                        "flex" => "-ms-flexbox",
146                        "inline-flex" => "-ms-inline-flexbox",
147                ],
148                "position" => [
149                        "sticky" => "-webkit-sticky",
150                ],
151        ];
152
153        // les eventuelles evaluation de condition avant remplacement
154        $prefix_conditions = [
155                "-webkit-text-decoration" => "prefixer_is_value_multiple",
156        ];
157
158        // et on lance
159        foreach ($list as $property=>$prefixed_list) {
160                        $css = prefixer_prefix_property($css, $property, $prefixed_list, $prefix_conditions, $values_replacement);
161        }
162        return $css;
163}
164
165/**
166 * Verifie si la valeur de la propriete est multiple (valeurs separees par des espaces)
167 * @param $line
168 * @return bool
169 */
170function prefixer_is_value_multiple($line) {
171        $value = explode(':', $line,2)[1];
172        $value = str_replace("!important", "", $value);
173        $value = trim($value,';}');
174        $value = trim($value);
175        if (count(explode(' ', $value))>1) {
176                return true;
177        }
178        return false;
179}
180
181/**
182 * Remplacer une valeur par une ou plusieurs autres, chaque remplacement effectif produisant une nouvelle ligne
183 * @param string $line
184 * @param array $replacement
185 * @return string
186 */
187function prefixer_replace_value($line, $replacement) {
188        $out = '';
189        list($base_property, $base_value) = explode(':', $line, 2);
190        foreach ($replacement as $standard_value => $prefixed_values) {
191                if (is_string($prefixed_values)) {
192                        $prefixed_values = [$prefixed_values];
193                }
194                foreach($prefixed_values as $prefixed_value) {
195                        $v = $base_value;
196                        if (strpos($base_value, $standard_value) !== false) {
197                                        $v = preg_replace(",(^|[\s]){$standard_value}($|\s|;|\))," , "$1$prefixed_value$2", $v);
198                        }
199                        if ($v !== $base_value) {
200                                $out .= $base_property . ':' . $v;
201                        }
202                }
203        }
204        if (!$out) {
205                return $line;
206        }
207        return $out;
208}
209
210/**
211 * Le parseur, qui recherche la propriete a prefixer et applique ensuite les remplacements necessaires
212 * @param string $css
213 * @param string $standard_property
214 * @param array $prefixed_list
215 * @param array $prefix_conditions
216 * @param array $values_replacement
217 * @return string
218 */
219function prefixer_prefix_property($css, $standard_property, $prefixed_list, $prefix_conditions, $values_replacement) {
220        $p = 0;
221        while (($p = strpos($css, $standard_property . ":", $p)) !== false) {
222                // go back to previous space or { or ;
223                $pstart = $pend = $p;
224                while ($pstart>0 && !in_array($css[$pstart-1], array("\r","\n",'{',';','('))) {
225                        $pstart--;
226                        if (!in_array($css[$pstart], array(" ", "\t"))) {
227                                $p+=strlen($standard_property);
228                                continue 2;
229                        }
230                }
231                while ($p>0 && in_array($css[$pstart-1], array("\r","\n"))) {
232                        $pstart--;
233                }
234                $len = strlen($css);
235                $endchars = array(';','}');
236                if ($css[$pstart-1] == '(') {
237                                $pstart--;
238                                $endchars[] = ')';
239                }
240                while ($pend<$len && !in_array($css[$pend],$endchars)) {
241                        $pend++;
242                }
243                $pend++;
244
245                $property_line = substr($css, $pstart, $pend - $pstart);
246                if (substr($property_line, -1) == '}') {
247                                $property_line = substr($property_line, 0, -1) . ';';
248                }
249                $is_expression = false;
250                // si on est entre des () c'est un @support ()
251                if (substr($property_line, 0, 1) == '(' && substr($property_line, -1) == ')') {
252                                $property_line = $property_line . " or ";
253                                $is_expression = true;
254                }
255                $insert = "";
256                foreach ($prefixed_list as $prefixed) {
257                        $line = str_replace("$standard_property:","$prefixed:", $property_line);
258                        if (!isset($prefix_conditions[$prefixed]) or $prefix_conditions[$prefixed]($line)) {
259                                        if (isset($values_replacement[$prefixed])) {
260                                                $line = prefixer_replace_value($line, $values_replacement[$prefixed]);
261                                        }
262                                        if ($line != $property_line) {
263                                                        $insert .= $line;
264                                        }
265                        }
266                }
267
268                if ($insert) {
269                        if (!$is_expression) {
270                                        $css = substr_replace($css, $insert, $pstart, 0);
271                        }
272                        else {
273                                        $property = substr($css, $pstart, $pend - $pstart);
274                                        $pstart++;
275                                        $pend--;
276                                        $insert = $insert . $property;
277                                        $css = substr_replace($css, $insert, $pstart, $pend - $pstart);
278                        }
279                }
280                $p = $pend + strlen($insert);
281        }
282        return $css;
283}
Note: See TracBrowser for help on using the repository browser.