source: spip-zone/_core_/plugins/compresseur/inc/compresseur_minifier.php @ 109752

Last change on this file since 109752 was 109752, checked in by spip.franck@…, 3 years ago

Bonne année aussi aux plugins-dist :-D

File size: 8.4 KB
Line 
1<?php
2
3/***************************************************************************\
4 *  SPIP, Systeme de publication pour l'internet                           *
5 *                                                                         *
6 *  Copyright (c) 2001-2018                                                *
7 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
8 *                                                                         *
9 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
10 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
11\***************************************************************************/
12
13/**
14 * Fonctions de minification
15 *
16 * @package SPIP\Compresseur\Minifier
17 */
18if (!defined("_ECRIRE_INC_VERSION")) {
19        return;
20}
21
22/**
23 * Minifier un contenu CSS
24 *
25 * Si $options est vide on utilise la methode regexp simple
26 *
27 * Si $options est une chaine non vide elle definit un media à appliquer
28 * à la css. Si la css ne contient aucun @media ni @import, on encapsule tout
29 * dans "@media $option {...}" et on utilise regexp sinon on utilise csstidy
30 * pour ne pas faire d'erreur, mais c'est 12 fois plus lent
31 *
32 * Si $options sous forme de array() on passe par csstidy pour parser le code
33 * et produire un contenu plus compact et prefixé eventuellement par un @media
34 * options disponibles :
35 * - string media : media qui seront utilisés pour encapsuler par @media
36 *   les selecteurs sans media
37 * - string template : format de sortie parmi 'low','default','high','highest'
38 *
39 * @param string $contenu
40 *     Contenu CSS
41 * @param mixed $options
42 *     Options de minification
43 * @return string
44 *     Contenu CSS minifié
45 */
46function minifier_css($contenu, $options = '') {
47        if (is_string($options) and $options) {
48                if ($options == "all") // facile : media all => ne rien preciser
49                {
50                        $options = "";
51                } elseif (
52                        strpos($contenu, "@media") == false
53                        and strpos($contenu, "@import") == false
54                        and strpos($contenu, "@font-face") == false
55                ) {
56                        $contenu = "@media $options {\n$contenu\n}\n";
57                        $options = "";
58                } else {
59                        $options = array('media' => $options);
60                }
61        }
62        if (!is_array($options)) {
63
64                // nettoyer la css de tout ce qui sert pas
65                // pas de commentaires
66                $contenu = preg_replace(",/\*.*\*/,Ums", "", $contenu);
67                $contenu = preg_replace(",\s//[^\n]*\n,Ums", "", $contenu);
68                // espaces autour des retour lignes
69                $contenu = str_replace("\r\n", "\n", $contenu);
70                $contenu = preg_replace(",\s+\n,ms", "\n", $contenu);
71                $contenu = preg_replace(",\n\s+,ms", "\n", $contenu);
72                // pas d'espaces consecutifs
73                $contenu = preg_replace(",\s(?=\s),Ums", "", $contenu);
74                // pas d'espaces avant et apres { ; ,
75                $contenu = preg_replace("/\s?({|;|,)\s?/ms", "$1", $contenu);
76                // supprimer les espaces devant : sauf si suivi d'une lettre (:after, :first...)
77                $contenu = preg_replace("/\s:([^a-z])/ims", ":$1", $contenu);
78                // supprimer les espaces apres :
79                $contenu = preg_replace("/:\s/ms", ":", $contenu);
80                // pas d'espaces devant }
81                $contenu = preg_replace("/\s}/ms", "}", $contenu);
82
83                // ni de point virgule sur la derniere declaration
84                $contenu = preg_replace("/;}/ms", "}", $contenu);
85                // pas d'espace avant !important
86                $contenu = preg_replace("/\s!\s?important/ms", "!important", $contenu);
87                // passser les codes couleurs en 3 car si possible
88                // uniquement si non precedees d'un [="'] ce qui indique qu'on est dans un filter(xx=#?...)
89                $contenu = preg_replace(";([:\s,(])#([0-9a-f])(\\2)([0-9a-f])(\\4)([0-9a-f])(\\6)(?=[^\w\-]);i", "$1#$2$4$6",
90                        $contenu);
91                // remplacer font-weight:bold par font-weight:700
92                $contenu = preg_replace("/font-weight:bold(?!er)/ims", "font-weight:700", $contenu);
93                // remplacer font-weight:normal par font-weight:400
94                $contenu = preg_replace("/font-weight:normal/ims", "font-weight:400", $contenu);
95
96                // si elle est 0, enlever la partie entière des unites decimales
97                $contenu = preg_replace("/\b0+\.(\d+em)/ims", ".$1", $contenu);
98                // supprimer les declarations vides
99                $contenu = preg_replace(",(^|})([^{}]*){},Ums", "$1", $contenu);
100                // zero est zero, quelle que soit l'unite (sauf pour % car casse les @keyframes cf https://core.spip.net/issues/3128)
101                $contenu = preg_replace("/([^0-9.]0)(em|px|pt)/ms", "$1", $contenu);
102
103                // renommer les couleurs par leurs versions courtes quand c'est possible
104                $colors = array(
105                        'source' => array(
106                                'black',
107                                'fuchsia',
108                                'white',
109                                'yellow',
110                                '#800000',
111                                '#ffa500',
112                                '#808000',
113                                '#800080',
114                                '#008000',
115                                '#000080',
116                                '#008080',
117                                '#c0c0c0',
118                                '#808080',
119                                '#f00'
120                        ),
121                        'replace' => array(
122                                '#000',
123                                '#F0F',
124                                '#FFF',
125                                '#FF0',
126                                'maroon',
127                                'orange',
128                                'olive',
129                                'purple',
130                                'green',
131                                'navy',
132                                'teal',
133                                'silver',
134                                'gray',
135                                'red'
136                        )
137                );
138                foreach ($colors['source'] as $k => $v) {
139                        $colors['source'][$k] = ";([:\s,(])" . $v . "(?=[^\w\-]);ms";
140                        $colors['replace'][$k] = "$1" . $colors['replace'][$k];
141                }
142                $contenu = preg_replace($colors['source'], $colors['replace'], $contenu);
143
144                // raccourcir les padding qui le peuvent (sur 3 ou 2 valeurs)
145                $contenu = preg_replace(",padding:([^\s;}]+)\s([^\s;}]+)\s([^\s;}]+)\s(\\2),ims", "padding:$1 $2 $3", $contenu);
146                $contenu = preg_replace(",padding:([^\s;}]+)\s([^\s;}]+)\s(\\1)([;}!]),ims", "padding:$1 $2$4", $contenu);
147
148                // raccourcir les margin qui le peuvent (sur 3 ou 2 valeurs)
149                $contenu = preg_replace(",margin:([^\s;}]+)\s([^\s;}]+)\s([^\s;}]+)\s(\\2),ims", "margin:$1 $2 $3", $contenu);
150                $contenu = preg_replace(",margin:([^\s;}]+)\s([^\s;}]+)\s(\\1)([;}!]),ims", "margin:$1 $2$4", $contenu);
151
152                $contenu = trim($contenu);
153
154                return $contenu;
155        } else {
156                // compression avancee en utilisant csstidy
157                // beaucoup plus lent, mais necessaire pour placer des @media la ou il faut
158                // si il y a deja des @media ou des @import
159
160                // modele de sortie plus ou moins compact
161                $template = 'high';
162                if (isset($options['template']) and in_array($options['template'], array('low', 'default', 'high', 'highest'))) {
163                        $template = $options['template'];
164                }
165                // @media eventuel pour prefixe toutes les css
166                // et regrouper plusieurs css entre elles
167                $media = "";
168                if (isset($options['media'])) {
169                        $media = "@media " . $options['media'] . " ";
170                }
171
172                include_spip("lib/csstidy/class.csstidy");
173                $css = new csstidy();
174
175                // essayer d'optimiser les font, margin, padding avec des ecritures raccourcies
176                $css->set_cfg('optimise_shorthands', 1);
177                $css->set_cfg('template', $template);
178                $css->parse($contenu);
179
180                return $css->print->plain($media);
181        }
182}
183
184
185/**
186 * Compacte du javascript grace a Dean Edward's JavaScriptPacker
187 *
188 * Bench du 15/11/2010 sur jQuery.js :
189 * JSMIN (https://github.com/rgrove/jsmin-php/)
190 *      61% de la taille initiale / 2 895 ms
191 * JavaScriptPacker
192 *  62% de la taille initiale / 752 ms
193 *
194 * @param string $flux
195 *     Contenu JS
196 * @return string
197 *     Contenu JS minifié
198 */
199function minifier_js($flux) {
200        if (!strlen($flux)) {
201                return $flux;
202        }
203
204        include_spip('lib/JavascriptPacker/class.JavaScriptPacker');
205        $packer = new JavaScriptPacker($flux, 0, true, false);
206
207        // en cas d'echec (?) renvoyer l'original
208        if (!strlen($t = $packer->pack())) {
209                spip_log('erreur de minifier_js', _LOG_INFO_IMPORTANTE);
210
211                return $flux;
212        }
213
214        return $t;
215}
216
217
218/**
219 * Une callback applicable sur chaque balise link qui minifie un fichier CSS
220 *
221 * @param string $contenu
222 * @param  string $balise
223 * @return string
224 */
225function callback_minifier_css_file($contenu, $balise) {
226        return minifier_css($contenu, extraire_attribut($balise, 'media'));
227}
228
229/**
230 * Une callback applicable sur chaque balise script qui minifie un JS
231 *
232 * @param string $contenu
233 * @param  string $balise
234 * @return string
235 */
236function callback_minifier_js_file($contenu, $balise) {
237        return minifier_js($contenu);
238}
239
240
241/**
242 * Minifier du HTML
243 *
244 * @param string $flux HTML à compresser
245 * @return string       HTML compressé
246 */
247function minifier_html($flux) {
248
249        // si pas de contenu ni de balise html, ne rien faire
250        if (!strlen($flux) or strpos($flux, "<") === false) {
251                return $flux;
252        }
253
254        static $options = null;
255        if (is_null($options)) {
256                $options = array();
257                if ($GLOBALS['meta']['auto_compress_css'] == 'oui') {
258                        $options['cssMinifier'] = 'minifier_css';
259                }
260                if ($GLOBALS['meta']['auto_compress_js'] == 'oui') {
261                        $options['jsMinifier'] = 'minifier_js';
262                }
263                include_spip('lib/minify_html/class.minify_html');
264        }
265
266        return Minify_HTML_SPIP::minify($flux, $options);
267}
Note: See TracBrowser for help on using the repository browser.