[SPIP] v3.2.1-->v3.2.3
[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-2019 *
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 $nom = $dir . md5(serialize($files) . serialize($callbacks)) . ".$format";
62 if (
63 (defined('_VAR_MODE') and _VAR_MODE == 'recalcul')
64 or !file_exists($nom)
65 ) {
66 $fichier = "";
67 $comms = array();
68 $total = 0;
69 $files2 = false;
70 foreach ($files as $key => $file) {
71 if (!is_array($file)) {
72 // c'est un fichier
73 $comm = $file;
74 // enlever le timestamp si besoin
75 $file = preg_replace(",[?].+$,", '', $file);
76
77 // preparer le fichier si necessaire
78 if ($callback_pre) {
79 $file = $callback_pre($file);
80 }
81
82 lire_fichier($file, $contenu);
83 } else {
84 // c'est un squelette
85 if (!isset($file[1])) {
86 $file[1] = '';
87 }
88 $comm = _SPIP_PAGE . "=$file[0]"
89 . (strlen($file[1]) ? "($file[1])" : '');
90 parse_str($file[1], $contexte);
91 $contenu = recuperer_fond($file[0], $contexte);
92
93 // preparer le contenu si necessaire
94 if ($callback_pre) {
95 $contenu = $callback_pre($contenu,
96 url_absolue(_DIR_RESTREINT ? generer_url_public($file[0], $file[1]) : $url_base));
97 }
98 // enlever le var_mode si present pour retrouver la css minifiee standard
99 if (strpos($file[1], 'var_mode') !== false) {
100 if (!$files2) {
101 $files2 = $files;
102 }
103 $old_key = $key;
104 $key = preg_replace(',(&(amp;)?)?var_mode=[^&\'"]*,', '', $key);
105 $file[1] = preg_replace(',&?var_mode=[^&\'"]*,', '', $file[1]);
106 if (!strlen($file[1])) {
107 unset($file[1]);
108 }
109 $files2 = array_replace_key($files2, $old_key, $key, $file);
110 }
111 }
112 // passer la balise html initiale en second argument
113 $fichier .= "/* $comm */\n" . $callback_min($contenu, $key) . "\n\n";
114 $comms[] = $comm;
115 $total += strlen($contenu);
116 }
117
118 // calcul du % de compactage
119 $pc = intval(1000 * strlen($fichier) / $total) / 10;
120 $comms = "compact [\n\t" . join("\n\t", $comms) . "\n] $pc%";
121 $fichier = "/* $comms */\n\n" . $fichier;
122
123 // si on a nettoye des &var_mode=recalcul : mettre a jour le nom
124 // on ecrit pas dans le nom initial, qui est de toute facon recherche qu'en cas de recalcul
125 // donc jamais utile
126 if ($files2) {
127 $files = $files2;
128 $nom = $dir . md5(serialize($files) . serialize($callbacks)) . ".$format";
129 }
130
131 $nom_tmp = $nom;
132 $final_callback = (isset($callbacks['all_min']) ? $callbacks['all_min'] : false);
133 if ($final_callback) {
134 unset($callbacks['all_min']);
135 $nom_tmp = $dir . md5(serialize($files) . serialize($callbacks)) . ".$format";
136 }
137 // ecrire
138 ecrire_fichier($nom_tmp, $fichier, true);
139 clearstatcache(true, $nom_tmp);
140 // ecrire une version .gz pour content-negociation par apache, cf. [11539]
141 ecrire_fichier("$nom_tmp.gz", $fichier, true);
142
143 if ($final_callback) {
144 // closure compiler ou autre super-compresseurs
145 // a appliquer sur le fichier final
146 $encore = $final_callback($nom_tmp, $nom);
147 // si echec, on se contente de la compression sans cette callback
148 if ($encore !== $nom) {
149 // ecrire
150 ecrire_fichier($nom, $fichier, true);
151 clearstatcache(true, $nom);
152 // ecrire une version .gz pour content-negociation par apache, cf. [11539]
153 ecrire_fichier("$nom.gz", $fichier, true);
154 }
155 }
156 }
157
158
159 }
160
161 // Le commentaire detaille n'apparait qu'au recalcul, pour debug
162 return array($nom, (isset($comms) and $comms) ? "<!-- $comms -->\n" : '');
163 }
164
165 /**
166 * Une callback pour la minification par défaut
167 *
168 * Mais justement, par défaut on ne minifie rien !
169 *
170 * @param string $contenu Contenu à minifier
171 * @return string Contenu à minifier
172 */
173 function &concatener_callback_identite(&$contenu) {
174 return $contenu;
175 }
176
177 /**
178 * Une callback pour ?
179 *
180 * @param array $tableau
181 *
182 * @param string $orig_key
183 * Index dont on cherche la valeur actuelle
184 * @param string $new_key
185 * Nouvel index que l'on veut voir affecter de la valeur de la clé d'origine
186 * @param mixed $new_value
187 * Si rempli, la nouvelle clé prend cette valeur à la place
188 * de la valeur de la clé d'origine
189 * @return array
190 *
191 */
192 function &array_replace_key($tableau, $orig_key, $new_key, $new_value = null) {
193 $t = array();
194 foreach ($tableau as $k => $v) {
195 if ($k == $orig_key) {
196 $k = $new_key;
197 if (!is_null($new_value)) {
198 $v = $new_value;
199 }
200 }
201 $t[$k] = $v;
202 }
203
204 return $t;
205 }