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

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

Améliorer la vérification de la configuration d'un cache.

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