c29d558a85d3234db497b4b2df8dfa0ffa5b12e0
[lhc/web/www.git] / www / plugins-dist / compresseur / inc / compresseur_concatener.php
1 <?php
2
3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
5 * *
6 * Copyright (c) 2001-2020 *
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 pour concaténer plusieurs fichiers en un
15 *
16 * @package SPIP\Compresseur\Concatener
17 */
18 if (!defined("_ECRIRE_INC_VERSION")) {
19 return;
20 }
21
22
23 /**
24 * Concaténer en un seul une liste de fichier,
25 * avec appels de callback sur chaque fichier,
26 * puis sur le fichier final
27 *
28 * Gestion d'un cache : le fichier concaténé n'est produit que si il n'existe pas
29 * pour la liste de fichiers fournis en entrée
30 *
31 *
32 * @param array $files
33 * Liste des fichiers à concatener, chaque entrée sour la forme html=>fichier
34 * - string $key : html d'insertion du fichier dans la page
35 * - string|array $fichier : chemin du fichier, ou tableau (page,argument) si c'est un squelette
36 * @param string $format
37 * js ou css utilisé pour l'extension du fichier de sortie
38 * @param array $callbacks
39 * Tableau de fonctions à appeler :
40 * - each_pre : fonction de préparation à appeler sur le contenu de chaque fichier
41 * - each_min : fonction de minification à appeler sur le contenu de chaque fichier
42 * - all_min : fonction de minification à appeler sur le contenu concatene complet, en fin de traitement
43 * @return array
44 * Tableau a 2 entrées retournant le nom du fichier et des commentaires HTML à insérer dans la page initiale
45 */
46 function concatener_fichiers($files, $format = 'js', $callbacks = array()) {
47 $nom = "";
48 if (!is_array($files) && $files) {
49 $files = array($files);
50 }
51 if (count($files)) {
52 $callback_min = isset($callbacks['each_min']) ? $callbacks['each_min'] : 'concatener_callback_identite';
53 $callback_pre = isset($callbacks['each_pre']) ? $callbacks['each_pre'] : '';
54 $url_base = self('&');
55
56 // on trie la liste de files pour calculer le nom
57 // necessaire pour retomber sur le meme fichier
58 // si on renome une url a la volee pour enlever le var_mode=recalcul
59 // mais attention, il faut garder l'ordre initial pour la minification elle meme !
60 $dir = sous_repertoire(_DIR_VAR, 'cache-' . $format);
61 list($nom, $lastmodified) = concatener_nom_fichier_concat($dir, $files, $callbacks, $format);
62 if (
63 (defined('_VAR_MODE') and _VAR_MODE == 'recalcul')
64 or !file_exists($nom)
65 or filemtime($nom) < $lastmodified
66 ) {
67 spip_log("concatener_fichiers: Recalculer $nom plus a jour", "compresseur" . _LOG_DEBUG);
68 $fichier = "";
69 $comms = array();
70 $total = 0;
71 $files2 = false;
72 foreach ($files as $key => $file) {
73 if (!is_array($file)) {
74 // c'est un fichier
75 $comm = $file;
76 // enlever le timestamp si besoin
77 $file = preg_replace(",[?].+$,", '', $file);
78
79 // preparer le fichier si necessaire
80 if ($callback_pre) {
81 $file = $callback_pre($file);
82 }
83
84 lire_fichier($file, $contenu);
85 } else {
86 // c'est un squelette
87 if (!isset($file[1])) {
88 $file[1] = '';
89 }
90 $comm = _SPIP_PAGE . "=$file[0]"
91 . (strlen($file[1]) ? "($file[1])" : '');
92 parse_str($file[1], $contexte);
93 $contenu = recuperer_fond($file[0], $contexte);
94
95 // preparer le contenu si necessaire
96 if ($callback_pre) {
97 $contenu = $callback_pre($contenu,
98 url_absolue(_DIR_RESTREINT ? generer_url_public($file[0], $file[1]) : $url_base));
99 }
100 // enlever le var_mode si present pour retrouver la css minifiee standard
101 if (strpos($file[1], 'var_mode') !== false) {
102 if (!$files2) {
103 $files2 = $files;
104 }
105 $old_key = $key;
106 $key = preg_replace(',(&(amp;)?)?var_mode=[^&\'"]*,', '', $key);
107 $file[1] = preg_replace(',&?var_mode=[^&\'"]*,', '', $file[1]);
108 if (!strlen($file[1])) {
109 unset($file[1]);
110 }
111 $files2 = array_replace_key($files2, $old_key, $key, $file);
112 }
113 }
114 // passer la balise html initiale en second argument
115 $fichier .= "/* $comm */\n" . $callback_min($contenu, $key) . "\n\n";
116 $comms[] = $comm;
117 $total += strlen($contenu);
118 }
119
120 // calcul du % de compactage
121 $pc = intval(1000 * strlen($fichier) / $total) / 10;
122 $comms = "compact [\n\t" . join("\n\t", $comms) . "\n] $pc%";
123 $fichier = "/* $comms */\n\n" . $fichier;
124
125 // si on a nettoye des &var_mode=recalcul : mettre a jour le nom
126 // on ecrit pas dans le nom initial, qui est de toute facon recherche qu'en cas de recalcul
127 // donc jamais utile
128 if ($files2) {
129 $files = $files2;
130 list($nom, $lastmodified) = concatener_nom_fichier_concat($dir, $files, $callbacks, $format);
131 }
132
133 $nom_tmp = $nom;
134 $final_callback = (isset($callbacks['all_min']) ? $callbacks['all_min'] : false);
135 if ($final_callback) {
136 unset($callbacks['all_min']);
137 list($nom_tmp, $lastmodified) = concatener_nom_fichier_concat($dir, $files, $callbacks, $format);
138 }
139 // ecrire
140 ecrire_fichier($nom_tmp, $fichier, true);
141 clearstatcache(true, $nom_tmp);
142 // ecrire une version .gz pour content-negociation par apache, cf. [11539]
143 ecrire_fichier("$nom_tmp.gz", $fichier, true);
144
145 if ($final_callback) {
146 // closure compiler ou autre super-compresseurs
147 // a appliquer sur le fichier final
148 $encore = $final_callback($nom_tmp, $nom);
149 // si echec, on se contente de la compression sans cette callback
150 if ($encore !== $nom) {
151 // ecrire
152 ecrire_fichier($nom, $fichier, true);
153 clearstatcache(true, $nom);
154 // ecrire une version .gz pour content-negociation par apache, cf. [11539]
155 ecrire_fichier("$nom.gz", $fichier, true);
156 }
157 }
158 }
159
160
161 }
162
163 // Le commentaire detaille n'apparait qu'au recalcul, pour debug
164 return array($nom, (isset($comms) and $comms) ? "<!-- $comms -->\n" : '');
165 }
166
167 /**
168 * Calculer le nom de fichier concatene
169 * en tenant compte des timestamps :
170 * un changement de timestamp ne doit pas modifier le nom mais bien forcer une mise a jour du fichier concat si besoin
171 * @param string $dir
172 * @param array $files
173 * @param array $callbacks
174 * @param string $format
175 * @return array
176 */
177 function concatener_nom_fichier_concat($dir, $files, $callbacks, $format) {
178 $lastmodified = 0;
179 $file_wo_timestamp = [];
180 // on ignore les cles : seul le fichier inclu compte, pas la forme exacte de la balise html dans laquelle il est insere
181 foreach ($files as $k => $file) {
182 if (!is_array($file)){
183 if (strpos($file, "?")!==false){
184 $file = explode('?', $file, 2);
185 if (is_numeric(end($file))){
186 $lastmodified = max($lastmodified, end($file));
187 }
188 $file = reset($file);
189 }
190 }
191 $file_wo_timestamp[] = $file;
192 }
193 $nom_fichier_concat = $dir . md5(json_encode($file_wo_timestamp) . json_encode($callbacks)) . ".$format";
194 return array($nom_fichier_concat, $lastmodified);
195 }
196
197 /**
198 * Une callback pour la minification par défaut
199 *
200 * Mais justement, par défaut on ne minifie rien !
201 *
202 * @param string $contenu Contenu à minifier
203 * @return string Contenu à minifier
204 */
205 function &concatener_callback_identite(&$contenu) {
206 return $contenu;
207 }
208
209 /**
210 * Une callback pour ?
211 *
212 * @param array $tableau
213 *
214 * @param string $orig_key
215 * Index dont on cherche la valeur actuelle
216 * @param string $new_key
217 * Nouvel index que l'on veut voir affecter de la valeur de la clé d'origine
218 * @param mixed $new_value
219 * Si rempli, la nouvelle clé prend cette valeur à la place
220 * de la valeur de la clé d'origine
221 * @return array
222 *
223 */
224 function &array_replace_key($tableau, $orig_key, $new_key, $new_value = null) {
225 $t = array();
226 foreach ($tableau as $k => $v) {
227 if ($k == $orig_key) {
228 $k = $new_key;
229 if (!is_null($new_value)) {
230 $v = $new_value;
231 }
232 }
233 $t[$k] = $v;
234 }
235
236 return $t;
237 }