source: spip-zone/_plugins_/cache/trunk/cache/cache.php @ 119649

Last change on this file since 119649 was 119649, checked in by Eric Lupinacci, 8 weeks ago

En fait d'amélioration c'était pas top. Là c'est mieux, la sécurisation est compatible avec la sérialisation voire le décodage

  • Property svn:eol-style set to native
File size: 18.7 KB
Line 
1<?php
2/**
3 * Ce fichier contient les fonctions de service du plugin Cache.
4 *
5 * Chaque fonction, soit aiguille, si elle existe, vers une fonction "homonyme" propre au plugin appelant
6 * soit déroule sa propre implémentation.
7 * Ainsi, les plugins externes peuvent, si elle leur convient, utiliser l'implémentation proposée par Cache Factory.
8 *
9 * @package SPIP\CACHE\SERVICE
10 */
11if (!defined('_ECRIRE_INC_VERSION')) {
12        return;
13}
14
15// -----------------------------------------------------------------------
16// ---------------------- SERVICES SURCHARGEABLES ------------------------
17// -----------------------------------------------------------------------
18
19/**
20 * Récupère la configuration des caches d'un plugin, la complète et la stocke dans une meta.
21 *
22 * Le plugin Cache Factory propose une configuration par défaut des caches.
23 *
24 * @uses cache_service_chercher()
25 * @uses lire_config()
26 * @uses ecrire_config()
27 *
28 * @param string $plugin
29 *                       Identifiant qui permet de distinguer le module appelant qui peut-être un plugin comme le noiZetier ou
30 *                       un script. Pour un plugin, le plus pertinent est d'utiliser le préfixe.
31 *
32 * @return array
33 *               Tableau de la configuration complétée des caches d'un plugin venant d'être enregistrée.
34 */
35function cache_cache_configurer($plugin) {
36
37        // Initialisation du tableau de configuration avec les valeurs par défaut du plugin Cache.
38        $configuration_defaut = array(
39                'racine'          => '_DIR_CACHE', // Emplacement de base du répertoire des caches. Attention c'est la chaine de la constante SPIP
40                'sous_dossier'    => false,        // Indicateur d'utilisation d'un sous-dossier
41                'nom_obligatoire' => array('nom'), // Composants obligatoires ordonnés de gauche à droite.
42                'nom_facultatif'  => array(),      // Composants facultatifs
43                'separateur'      => '',           // Caractère de séparation des composants du nom '_' ou '-' ou '' si un seul composant est utilisé
44                'extension'       => '.txt',       // Extension du fichier cache (vaut .php si cache sécurisé)
45                'securisation'    => false,        // Indicateur de sécurisation du fichier
46                'serialisation'   => true,         // Indicateur de sérialisation
47                'decodage'        => false,        // Permet d'appliquer une fonction de décodage à la lecture qui dépend de l'extension
48                'conservation'    => 0             // Durée de conservation du cache en secondes. 0 pour permanent
49        );
50
51        // Le plugin utilisateur doit fournir un service propre pour la configuration de ses caches.
52        // Cette configuration peut-être partielle, dans ce cas les données manquantes sont complétées
53        // par celles par défaut.
54        $configuration_plugin = array();
55        if ($configurer = cache_service_chercher($plugin, 'cache_configurer')) {
56                $configuration_plugin = $configurer($plugin);
57        }
58
59        // On merge la configuration du plugin avec celle par défaut pour assure la complétude.
60        $configuration = array_merge($configuration_defaut, $configuration_plugin);
61
62        // On vérifie que la durée de conservation du cache est bien un entier supérieur ou égal à 0.
63        // La durée est exprimée en secondes.
64        $configuration['conservation'] = abs(intval($configuration['conservation']));
65
66        // On vérifie en priorité la sécurisation. Si le cache doit être sécurisé :
67        // - le décodage n'est pas possible
68        // - l'extension du cache doit absolument être .php. Si ce n'est pas le cas on la force.
69        if ($configuration['securisation']) {
70                $configuration['decodage'] = false;
71                if ($configuration['extension'] != '.php') {
72                        $configuration['extension'] = '.php';
73                }
74        }
75
76        // On vérifie ensuite la sérialisation. Si le cache est sérialisé :
77        // - le décodage n'est pas possible.
78        if ($configuration['serialisation']) {
79                $configuration['decodage'] = false;
80        }
81
82        // On vérifie en dernier le décodage. Si le cache demande un décodage :
83        // - sécurisation et sérialisation ne sont pas possibles mais ont été traitées précédemment
84        // - le cache n'accepte que les extensions : json, xml ou yaml.
85        if ($configuration['decodage']) {
86                if ((($configuration['extension'] == 'yaml') or ($configuration['extension'] == 'yml'))
87                and (!defined('_DIR_PLUGIN_YAML'))) {
88                        $configuration['decodage'] = false;
89                }
90        }
91
92        // Pour faciliter la construction du chemin des caches on stocke les éléments récurrents composant
93        // le dossier de base.
94        // -- Vérification de la localisation de la racine qui ne peut être que dans les trois dossiers SPIP
95        //    prévus.
96        if (!in_array($configuration['racine'], array('_DIR_CACHE', '_DIR_TMP', '_DIR_VAR'))) {
97                $configuration['racine'] = $configuration_defaut['racine'];
98        }
99        // -- Sous-dossier spécifique au plugin
100        $configuration['dossier_plugin'] = ($configuration['racine'] == '_DIR_VAR') ? "cache-${plugin}/" : "${plugin}/";
101
102        // Construction du tableau des composants du nom : dans l'ordre on a toujours les composants obligatoires
103        // suivis des composants facultatifs.
104        $configuration['nom'] = array_merge($configuration['nom_obligatoire'], $configuration['nom_facultatif']);
105
106        // Si le nom ne comporte qu'un seul composant forcer le séparateur à '' pour ne pas interdire d'utiliser les
107        // caractères '_' ou '-' dans le composant unique.
108        if (count($configuration['nom']) == 1) {
109                $configuration['separateur'] = '';
110        }
111
112        // Enregistrement de la configuration du plugin utilisateur dans la meta prévue.
113        // Si une configuration existe déjà on l'écrase.
114        include_spip('inc/config');
115        $meta_cache = lire_config('cache', array());
116        $meta_cache[$plugin] = $configuration;
117        ecrire_config('cache', $meta_cache);
118
119        return $configuration;
120}
121
122/**
123 * Construit le chemin complet du fichier cache.
124 *
125 * @uses cache_service_chercher()
126 *
127 * @param string $plugin
128 *                              Identifiant qui permet de distinguer le module appelant qui peut-être un plugin comme le noiZetier
129 *                              ou un script. Pour un plugin, le plus pertinent est d'utiliser le préfixe.
130 * @param array  $cache
131 *                              Tableau identifiant le cache pour lequel on veut construire le nom.
132 * @param array  $configuration
133 *                              Configuration complète des caches du plugin utlisateur lue à partir de la meta de stockage.
134 *
135 * @return string
136 */
137function cache_cache_composer($plugin, $cache, $configuration) {
138
139        // Le plugin utilisateur peut fournir un service propre pour construire le chemin complet du fichier cache.
140        // Néanmoins, étant donné la généricité du mécanisme offert par le plugin Cache cela devrait être rare.
141        if ($composer = cache_service_chercher($plugin, 'cache_composer')) {
142                $fichier_cache = $composer($plugin, $cache, $configuration);
143        } else {
144                // On utilise le mécanisme de nommage standard du plugin Cache.
145                // Initialisation du chemin complet du fichier cache
146                $fichier_cache = '';
147
148                // Détermination du répertoire final du fichier cache qui peut-être inclus dans un sous-dossier du dossier
149                // de base des caches du plugin.
150                $dir_cache = constant($configuration['racine']) . $configuration['dossier_plugin'];
151                if ($configuration['sous_dossier']) {
152                        if (!empty($cache['sous_dossier'])) {
153                                // Si le cache nécessite un sous-dossier, appelé sous_dossier dans l'identifiant du cache.
154                                $dir_cache .= rtrim($cache['sous_dossier'], '/') . '/';
155                        } else {
156                                // C'est une erreur, le sous-dossier n'a pas été fourni alors qu'il est requis.
157                                $dir_cache = '';
158                        }
159                }
160
161                // Détermination du nom du cache sans extension.
162                // Celui-ci est construit à partir des éléments fournis sur le cache et de la configuration
163                // fournie par le plugin (liste ordonnée de composant).
164                $nom_cache = '';
165                if ($dir_cache) {
166                        foreach ($configuration['nom'] as $_composant) {
167                                if (isset($cache[$_composant])) {
168                                        if (!$nom_cache) {
169                                                // Il y a forcément un composant non vide en premier.
170                                                $nom_cache .= $cache[$_composant];
171                                        } elseif ($cache[$_composant]
172                                                or (!$cache[$_composant] and in_array($_composant, $configuration['nom_obligatoire']))) {
173                                                // Le composant est à ajouter : non vide ou vide mais obligatoire (cas bizarre!)
174                                                $nom_cache .= $configuration['separateur'] . $cache[$_composant];
175                                        }
176                                }
177                        }
178                }
179
180                // Si le nom a pu être construit on finalise le chemin complet, sinon on renvoie une chaine vide.
181                if ($nom_cache) {
182                        // L'extension par défaut est dans la configuration mais peut-être forcée pour un cache donné.
183                        // Par contre, si le cache est sécurisé alors on ne tient pas compte du forçage éventuel car l'extension
184                        // doit toujours être .php et celle-ci a été forcée lors de la configuration des caches du plugin.
185                        $extension = (!empty($cache['extension']) and !$configuration['securisation'])
186                                ? $cache['extension']
187                                : $configuration['extension'];
188                        // Le chemin complet
189                        $fichier_cache = "${dir_cache}${nom_cache}${extension}";
190                }
191        }
192
193        return $fichier_cache;
194}
195
196/**
197 * Décompose le chemin complet du fichier cache en éléments constitutifs. Par défaut, le tableau obtenu coïncide
198 * avec l’identifiant relatif du cache. La fonction utilise la configuration générale pour connaitre la structure
199 * du chemin du fichier.
200 *
201 * Cache Factory renvoie uniquement les éléments de l'identifiant relatif.
202 *
203 * @uses cache_service_chercher()
204 *
205 * @param string $plugin
206 *                              Identifiant qui permet de distinguer le module appelant qui peut-être un plugin comme le noiZetier
207 *                              ou un script. Pour un plugin, le plus pertinent est d'utiliser le préfixe.
208 * @param string $fichier_cache
209 *                              Le chemin complet du fichier à phraser.
210 * @param array  $configuration
211 *                              Configuration complète des caches du plugin utlisateur lue à partir de la meta de stockage.
212 *
213 * @return array
214 *               Tableau des composants constitutifs du cache
215 */
216function cache_cache_decomposer($plugin, $fichier_cache, $configuration) {
217
218        // Le plugin utilisateur peut fournir un service propre pour construire le chemin complet du fichier cache.
219        // Néanmoins, étant donné la généricité du mécanisme offert par le plugin Cache cela devrait être rare.
220        if ($decomposer = cache_service_chercher($plugin, 'cache_decomposer')) {
221                $cache = $decomposer($plugin, $fichier_cache, $configuration);
222        } else {
223                // On utilise le mécanisme de nommage standard du plugin Cache. De fait, on considère qu'aucun composant
224                // n'est facultatif ou du moins qu'un seul composant est facultatif et positionné en dernier.
225
226                // Initialisation du tableau cache
227                $cache = array();
228
229                // On supprime le dossier de base pour n'avoir que la partie spécifique du cache.
230                $dir_cache = constant($configuration['racine']) . $configuration['dossier_plugin'];
231                $fichier_cache = str_replace($dir_cache, '', $fichier_cache);
232
233                // Détermination du nom du cache sans extension et décomposition suivant la configuration du nom.
234                $nom_cache = basename($fichier_cache, $configuration['extension']);
235                if (count($configuration['nom']) == 1) {
236                        // Le nom est composé d'un seul composant : on le renvoie directement.
237                        $cache[$configuration['nom'][0]] = $nom_cache;
238                } else {
239                        // Le nom est composé de plus d'un composant.
240                        foreach (explode($configuration['separateur'], $nom_cache) as $_cle => $_composant) {
241                                $cache[$configuration['nom'][$_cle]] = $_composant;
242                        }
243                }
244
245                // Identification d'un sous-dossier si il existe.
246                if ($configuration['sous_dossier'] and ($sous_dossier = dirname($fichier_cache))) {
247                        $cache['sous_dossier'] = $sous_dossier;
248                }
249        }
250
251        return $cache;
252}
253
254/**
255 * Complète la description d'un cache issue du service `cache_decomposer()`.
256 *
257 * Le plugin Cache Factory complète la description canonique avec le nom sans extension et l'extension du fichier.
258 *
259 * @uses cache_service_chercher()
260 *
261 * @param string $plugin
262 *                              Identifiant qui permet de distinguer le module appelant qui peut-être un plugin comme le noiZetier
263 *                              ou un script. Pour un plugin, le plus pertinent est d'utiliser le préfixe.
264 * @param array  $cache
265 *                              Tableau identifiant le cache pour lequel on veut construire le nom.
266 * @param string $fichier_cache
267 *                              Fichier cache désigné par son chemin complet.
268 * @param array  $configuration
269 *                              Configuration complète des caches du plugin utilisateur lue à partir de la meta de stockage.
270 *
271 * @return array
272 *               Description du cache complétée par un ensemble de données propres au plugin.
273 */
274function cache_cache_completer($plugin, $cache, $fichier_cache, $configuration) {
275
276        // Cache Factory complète la description avec le nom sans extension et l'extension du fichier cache avant
277        // de passer la main au plugin utilisateur.
278        $cache['nom_cache'] = basename($fichier_cache, $configuration['extension']);
279        $cache['extension_cache'] = $configuration['extension'];
280
281        // Le plugin utilisateur peut fournir un service propre pour construire le chemin complet du fichier cache.
282        // Néanmoins, étant donné la généricité du mécanisme offert par le plugin Cache cela devrait être rare.
283        if ($completer = cache_service_chercher($plugin, 'cache_completer')) {
284                $cache = $completer($plugin, $cache, $fichier_cache, $configuration);
285        }
286
287        return $cache;
288}
289
290/**
291 * Décode le contenu du fichier cache en fonction de l'extension.
292 *
293 * Le plugin Cache Factory utilise des fonctions standard de PHP, SPIP ou du plugin YAML. Un plugin appelant peut
294 * proposer une fonction spécifique de décodage
295 *
296 * @uses cache_service_chercher()
297 *
298 * @param string $plugin
299 *                              Identifiant qui permet de distinguer le module appelant qui peut-être un plugin comme le noiZetier
300 *                              ou un script. Pour un plugin, le plus pertinent est d'utiliser le préfixe.
301 * @param string $contenu
302 *                              Contenu du fichier cache au format chaine.
303 * @param array  $configuration
304 *                              Configuration complète des caches du plugin utilisateur lue à partir de la meta de stockage.
305 *
306 * @return array
307 *               Contenu du cache décodé si la fonction idoine a été appliqué ou tel que fourni en entrée sinon.
308 */
309function cache_cache_decoder($plugin, $contenu, $configuration) {
310
311        // Cache Factory décode le contenu du fichier cache en fonction de l'extension (json, yaml, yml ou xml).
312        $encodage = ltrim($configuration['extension'], '.');
313
314        // Le plugin utilisateur peut fournir un service propre pour décoder le contenu du cache.
315        // Néanmoins, étant donné la généricité du mécanisme offert par le plugin Cache cela devrait être rare.
316        if ($decoder = cache_service_chercher($plugin, "cache_decoder_${encodage}")) {
317                $contenu = $decoder($plugin, $contenu);
318        } else {
319                // Utilisation des fonctions génériques de Cache Factory
320                switch ($encodage) {
321                        case 'json':
322                                // On utilise la fonction PHP native
323                                $contenu = json_decode($contenu, true);
324                                break;
325                        case 'yaml':
326                        case 'yml':
327                                // On utilise la fonction du plugin YAML si il est actif (un jour on l'aura dans SPIP...)
328                                if (!defined('_DIR_PLUGIN_YAML')) {
329                                        include_spip('inc/yaml');
330                                        $contenu = yaml_decode($contenu);
331                                }
332                                break;
333                        case 'xml':
334                                // On utilise la fonction historique de SPIP sachant qu'il en existe d'autre. Pour changer il suffit
335                                // d'utiliser une fonction spécifique du plugin appelant.
336                                include_spip('inc/xml');
337                                $contenu = spip_xml_parse($contenu, true);
338                                break;
339                        default:
340                }
341        }
342
343        return $contenu;
344}
345
346/**
347 * Effectue le chargement du formulaire de vidage des caches pour un plugin utilisateur donné.
348 *
349 * Le plugin Cache Factory propose une version simplifié du formulaire où tous les fichiers caches
350 * sont listées par ordre alphabétique sans possibilité de regroupement.
351 *
352 * @uses cache_service_chercher()
353 * @uses cache_repertorier()
354 *
355 * @param string $plugin
356 *                              Identifiant qui permet de distinguer le module appelant qui peut-être un plugin comme le noiZetier
357 *                              ou un script. Pour un plugin, le plus pertinent est d'utiliser le préfixe.
358 * @param array  $options
359 *                              Tableau d'options qui peut être fourni par un plugin utilisateur uniquement si celui-ci fait appel
360 *                              au formulaire. La page cache_vider de Cache Factory n'utilise pas ce paramètre.
361 *                              Le tableau est passé à la fonction de service de chargement du formulaire uniquement.
362 * @param array  $configuration
363 *                              Configuration complète des caches du plugin utilisateur lue à partir de la meta de stockage.
364 *
365 * @return array
366 *               Description du cache complétée par un ensemble de données propres au plugin.
367 */
368function cache_formulaire_charger($plugin, $options, $configuration) {
369
370        // Stocker le préfixe et le nom du plugin de façon systématique.
371        $valeurs = array('_prefixe' => $plugin);
372        $informer = chercher_filtre('info_plugin');
373        $valeurs['_nom_plugin'] = $informer($plugin, 'nom', true);
374
375        // Le plugin utilisateur peut fournir un service propre pour construire le tableau des valeurs du formulaire.
376        if ($charger = cache_service_chercher($plugin, 'formulaire_charger')) {
377                $valeurs_plugin = $charger($plugin, $options, $configuration);
378                if ($valeurs_plugin) {
379                        $valeurs = array_merge($valeurs, $valeurs_plugin);
380                }
381        } else {
382                // On présente simplement les fichiers caches en ordre alphabétique en visualisant uniquement
383                // le sous-dossuer éventuel et le nom du fichier sans décomposition.
384                $valeurs['_caches'] = cache_repertorier($plugin, array());
385        }
386
387        return $valeurs;
388}
389
390// -----------------------------------------------------------------------
391// ----------------- UTILITAIRE PROPRE AU PLUGIN CACHE -------------------
392// -----------------------------------------------------------------------
393
394/**
395 * Cherche une fonction donnée en se basant sur le plugin appelant.
396 * Si le plugin utilisateur ne fournit pas la fonction demandée la chaîne vide est renvoyée.
397 *
398 * @internal
399 *
400 * @param string $plugin
401 *                         Identifiant qui permet de distinguer le module appelant qui peut-être un plugin comme le noiZetier ou
402 *                         un script. Pour un plugin, le plus pertinent est d'utiliser le préfixe.
403 * @param bool   $fonction
404 *                         Nom de la fonction de service à chercher.
405 *
406 * @return string
407 *                Nom complet de la fonction si trouvée ou chaine vide sinon.
408 */
409function cache_service_chercher($plugin, $fonction) {
410        $fonction_trouvee = '';
411
412        // Eviter la réentrance si on demande explicitement le service du plugin Cache Factory.
413        if ($plugin != 'cache') {
414                include_spip("cache/${plugin}");
415                $fonction_trouvee = "${plugin}_${fonction}";
416                if (!function_exists($fonction_trouvee)) {
417                        $fonction_trouvee = '';
418                }
419        }
420
421        return $fonction_trouvee;
422}
Note: See TracBrowser for help on using the repository browser.