Cache Factory

Cache Factory propose une API fonctionnelle PHP permettant de configurer, d’écrire, de lire et de supprimer des caches et une interface utilisateur dans l’espace privé limitée à un formulaire de vidage des caches. C’est un outil de développement pour plugins.

Dans sa version actuelle Cache Factory ne gère que des caches fichier.

Concepts

Les caches

Les caches sont des espaces secondaires de stockage de données dont le but est de fournir un accès plus rapide que le stockage principal de ces mêmes données. Un mécanisme de mise à jour des caches doit être mis en œuvre afin de suivre les modifications du stockage principal : les caches ont en général une durée de vie limitée dans le temps.

Un cache est soit un fichier soit un espace mémoire. Dans cette version du plugin Cache Factory les caches sont toujours des fichiers.

Les caches « fichier »

Identification complète d’un fichier cache

Le plugin Cache Factory fournit une API qui permet de simplifier la manipulation (écriture, lecture, suppression, test d’existence…) de fichiers cache pour un plugin utilisateur. Un fichier est identifié de façon unique par un chemin sur le disque. Le chemin des caches de Cache Factory est toujours formaté selon la grammaire ABNF (approximative) suivante :

<chemin> = <repertoire-plugin> [<sous-dossier>] <nom-fichier> <extension-fichier>

<repertoire-plugin>    = <racine> <sous-dossier-plugin> "/"
<racine>               = (_DIR_CACHE / _DIR_VAR / _DIR_TMP)  ; constante SPIP
<sous-dossier-plugin > = ["cache-"]<prefixe-plugin>          ; suivant la racine
<prefixe-plugin>       = ALPHA *(ALPHA / DIGIT / "_")

<sous-dossier>         = 1*(ALPHA / DIGIT / "-" / "_") "/"
<nom_fichier>          = <composant> *(<separateur> <composant>)
<composant>            = 1*(ALPHA / DIGIT / "-" / "_")       ; sauf le séparateur
<separateur>           = ("-" / "_")
<extension-fichier>    = "."  1*(ALPHA / DIGIT)

ALPHA                  =  %x41-5A / %x61-7A                  ; A-Z / a-z
DIGIT                  =  %x30-39                            ; 0-9

L’interprétation de la grammaire formelle permet d’édicter les règles suivantes :

  • un fichier cache est toujours localisé dans un répertoire propre au plugin utilisateur (<repertoire-plugin>) qui est un sous-dossier des répertoires standards dans lesquels SPIP a déjà l’habitude de stocker ses propres caches (_DIR_CACHE, _DIR_VAR et _DIR_TMP) ;
  • l’identification du cache à l’intérieur du répertoire du plugin utilisateur est donné par un sous-dossier facultatif et un nom qui est composé d’une série ordonnée de composants sémantiques prédéfinis ;
  • un composant du nom d’un fichier cache ne peut pas contenir le caractère séparateur utilisé.

Par exemple, les caches de N-Core sont stockés dans un dossier tmp/cache/ncore/ et se nomment <sous-dossier>/<objet>-<fonction>.php. Cela donne pour le cache Ajax du noiZetier : tmp/cache/ncore/noizetier/type_noisette-ajax.php. Le tiret est utilisé comme séparateur car le composant <objet> peut contenir le caractère souligné.

Identification relative d’un cache

Pour simplifier l’identification d’un cache pour les plugins utilisateur, Cache Factory ne demande pas à chaque appel d’une API tous les éléments d’identification décrits au paragraphe précédent. La racine, le séparateur et l’extension sont des éléments invariables du chemin du fichier cache d’un plugin utilisateur donné et sont stockés dans une configuration générale.

Ainsi, connaissant le plugin utilisateur - ce qui est le cas dans chaque fonction d’API - l’identification relative d’un cache est un tableau associatif fournissant le sous-dossier - si utilisé - et les composants, chaque composant portant une sémantique spécifique. Dans l’exemple du paragraphe précédent tiré du plugin N-Core, l’identifiant relatif coïncide avec le tableau suivant :

sous_dossier Reçoit le préfixe du plugin utilisateur de N-Core "noizetier"
objet Identifie l’objet de N-Core concerné par le cache "type_noisette"
fonction Identifie le type de cache pour l’objet concerné, en l’occurrence dans notre cas, les indicateurs Ajax des types de noisette. "ajax"

Un plugin utilisateur doit manipuler de façon préférentielle l’identifiant relatif et rarement son chemin complet.

Les attributs des caches

Les caches fichier possèdent dans la version actuelle du plugin trois attributs : l’un indique si le cache est sécurisé (au sens de l’API SPIP), l’autre précise si le contenu est sérialisé et le dernier donne la durée de conservation du cache. Ces attributs sont stockés dans la configuration générale.

API fonctionnelle PHP

La gestion des caches consiste principalement à définir le nom des caches à partir de la configuration générale pour un plugin utilisateur et de l’identification relative, et de gérer les écritures et lectures de façon à simplifier au maximum les appels et traitements de l’appelant. Aucune donnée n’est enregistrée par cache, seule la configuration générale est stockée en meta.

API : inc/cache.php
Fonctions liées aux caches
cache_ecrire Ecrit un contenu donné dans un cache spécifié par son identifiant relatif ou par son chemin complet.
cache_est_valide Teste l’existence d’un cache sur le disque spécifié par son identifiant relatif ou par son chemin complet et, si il existe, teste si la date d’expiration du fichier n’est pas dépassée. Si le fichier existe et n’est pas périmé, la fonction renvoie le chemin complet, sinon elle renvoie une chaine vide.
cache_lire Lit le cache spécifié par son identifiant relatif ou son chemin complet et renvoie le contenu sous forme de tableau ou de chaine suivant l’attribut de sérialisation. En cas d’erreur, la fonction renvoie false.
cache_nommer Renvoie le chemin complet du cache sans tester son existence. Cette fonction est une encapsulation du service cache_cache_composer(). Son utilisation par un plugin appelant doit rester limitée.
cache_repertorier Retourne la description des caches d’un plugin utilisateur filtrée sur un ensemble de critères. La description de base fournie par Cache Factory contient les éléments de l’identifiant relatif mais peut-être remplacée ou complétée par le plugin appelant au travers de services propres. Les filtres concernent uniquement les éléments de l’identifiant relatif.
cache_supprimer Supprime le cache spécifié par son identifiant relatif ou par son chemin complet.
cache_vider Supprime, pour un plugin utilisateur donné, les caches désignés par leur chemin complet.
Fonctions liées à la configuration
configuration_cache_lire Lit la configuration générale des caches d’un plugin utilisateur ou de tous les plugins utilisateur ayant enregistrés une configuration.
configuration_cache_effacer Efface la configuration générale des caches d’un plugin utilisateur ou de tous les plugins utilisateur ayant enregistrés une configuration.

L’interface utilisateur de vidage des caches

L’interface utilisateur de vidage des caches est composée d’une page dans l’espace privé, cache_vider, et d’un formulaire listant les caches d’un plugin utilisateur donné, #FORMULAIRE_CACHE_VIDER.

La page cache_vider affiche les formulaires de vidage de chaque plugin utilisateur les uns sous les autres comme illustré ci-après.

Le formulaire de vidage possède un paramètre obligatoire, l’identifiant du plugin utilisateur. Le prototype est le suivant : #FORMULAIRE_CACHE_VIDER{plugin[, options]}.

Chaque formulaire est personnalisable par plugin utilisateur en surchargeant le service de chargement des paramètres du formulaire et le sous-squelette d’affichage de la liste caches. Cette personnalisation est discutée au chapitre suivant.

Fonctionnement de Cache factory

La dissociation API – Services

Le fonctionnement global du plugin Cache Factory s’appuie sur une architecture API - services. Par conception, Cache Factory dissocie la fonction d’API, des services propres à un plugin utilisateur qui en personnalisent la gestion.

Dans le code de ses API, Cache Factory appelle donc des fonctions de service qui :

  • si la fonction de service homonyme existe dans le plugin utilisateur, va l’appeler et l’utiliser ;
  • sinon, va dérouler la fonction de service de Cache Factory.

L’aiguillage des services

Par conception et pour des raisons de lisibilité du code, les fonctions d’API de Cache Factory appellent systématiquement les fonctions de service de Cache Factory. Ce sont les fonctions de service de Cache Factory qui réalisent l’aiguillage vers le service souhaité ce qui leur permet aussi d’effectuer des traitements génériques et donc de limiter encore plus la complexité pour les plugins utilisateur.

Les services

Les fonctions d’API de Cache Factory font donc appel à des services dont la liste exacte est fournie ci-après. Le nom des fonctions est amputé du préfixe du plugin appelant. Ces fonctions de service ne doivent pas être appelées par le plugin appelant qui doit utiliser exclusivement les fonctions d’API. Le plugin appelant peut définir ses propres services qui seront appelés par ceux de Cache Factory, mais en aucun cas les utiliser dans son code

Services personnalisables
Services de configuration
cache_configurer(*) Renvoie la configuration générale des caches d’un plugin utilisateur sous la forme d’un tableau associatif dont les index sont standardisés.
Services de gestion des identifiants de cache
cache_composer Construit le chemin complet du fichier cache à partir du tableau de l’identifiant relatif du cache.
cache_decomposer Décompose le chemin complet du fichier cache en éléments constitutifs. Par défaut, le tableau obtenu coïncide avec l’identifiant relatif du cache. La fonction utilise la configuration générale pour connaitre la structure du chemin du fichier.
cache_completer Complète la description canonique d’un cache issue du service cache_decomposer(). Le plugin Cache Factory complète la description canonique avec le nom sans extension et l’extension du fichier.
Services de gestion du formulaire de vidage
formulaire_charger Effectue le chargement du formulaire de vidage des caches pour un plugin utilisateur donné. Par défaut, le plugin Cache Factory propose une version simplifiée du formulaire où tous les fichiers caches sont listés par ordre alphabétique sans possibilité de regroupement.

Les services notés (*) doivent toujours être définies par le plugin utilisateur, les autres sont optionnels.

Cache Factory propose l’ensemble des services dans son fichier cache/cache.php ce qui permet de minimiser les développements pour la plupart des plugins utilisateur. A minima, un plugin utilisateur peut se contenter de décrire uniquement sa configuration générale si il n’a aucune spécificité par rapport aux services natifs de Cache Factory.

Par exemple, le plugin Rainette définit la configuration générale de ses caches et personnalise le chargement du formulaire de vidage des caches en codant dans le fichier cache/rainette.php les fonctions rainette_cache_configurer() et rainette_formulaire_charger().

Mise en œuvre dans un plugin utilisateur

Déclarer la configuration générale

Cache Factory a besoin de connaitre la configuration générale des caches d’un plugin utilisateur pour fonctionner. Le plugin utilisateur doit obligatoirement déclarer sa configuration par l’intermédiaire du service cache_configurer().

Le tableau associatif de la configuration à fournir par un plugin utilisateur a une structure prédéfinie qui est décrite ci-après. Le plugin utilisateur peut fournir tout ou partie de cette configuration sachant que les index non fournis seront complétés par Cache Factory avec une valeur par défaut.

Par exemple, le plugin N-Core déclare la configuration suivante :

$configuration = array(
   'racine'          => '_DIR_CACHE',
   'sous_dossier'    => true,
   'nom_obligatoire' => array('objet', 'fonction'),
   'nom_facultatif'  => array(),
   'extension'       => '.php',
   'securisation'    => true,
   'serialisation'   => true,
   'separateur'      => '-',
   'conservation'    => 0
);

Le tableau suivant récapitule l’ensemble des paramètres de configuration et leur valeur par défaut.

Paramètres de configuration
Paramètres fournis par le plugin utilisateur
racine Emplacement de base dans lequel sera créé le répertoire de stockage des caches du plugin utilisateur.. Les valeurs possibles sont les chaines "_DIR_CACHE", "_DIR_VAR" et "_DIR_TMP". "_DIR_CACHE"
sous_dossier Indique si le cache est inclus dans un sous-dossier du répertoire de stockage du plugin. false
nom_obligatoire Tableau des composants obligatoires et ordonnés du nom d’un cache. Le nom des composants sert d’index quand il s’agit de fournir le tableau identifiant un cache. array("nom")
nom_facultatif Tableau des composants facultatifs et ordonnés du nom d’un cache. Les composants facultatifs suivent toujours les composants obligatoires. array()
separateur Caractère de séparation de chaque composant obligatoire ou facultatif du nom. Prend les valeurs "_", "-" ou "" si le nom est constitué d’un seul composant. Ce caractère ne doit pas être utilisé dans les composants du nom. ""
extension Extension du fichier cache. Si le fichier cache est sécurisé l’extension est toujours forcée à ".php". ".txt"
securisation Indique si le fichier doit être sécurisé ou pas. false
serialisation Indique si le contenu à stocker est un tableau à sérialiser ou pas. true
conservation Durée de conservation du cache exprimée en secondes. La valeur 0 est utilisée pour indiquer que le cache est permanent. 0
Paramètres calculées par Cache Factory
dossier_plugin Répertoire de stockage du plugin calculé à partir de la racine et du préfixe du plugin. Si la racine prend la valeur "_DIR_VAR" alors le répertoire du plugin est un sous-dossier de nom "cache-${plugin}/". Sinon, le sous-dossier porte le nom du préfixe du plugin. "${plugin}/"
nom Tableau regroupant dans l’ordre les noms obligatoires puis les noms facultatifs. array("nom")

Cache Factory utilise une meta nommée cache pour stocker l’ensemble des configurations des plugins utilisateur.

Personnaliser le formulaire de vidage des caches

Cache Factory permet de personnaliser la présentation des caches d’un plugin utilisateur dans le formulaire de vidage de ces mêmes caches.

En effet, par défaut, Cache Factory affiche une liste unique de tous les caches du plugin utilisateur avec le sous-dossier, si il existe, et le nom du cache sans son extension. Les caches sont classés alphabétiquement. Cette présentation par défaut suffit au plugin N-Core mais pas à Rainette qui préfère présenter ses caches regroupés par service.

Pour permettre à Rainette et à d’autres plugins de personnaliser l’affichage, la portion du squelette qui réalise l’affichage de la liste est codée dans une inclusion. Par défaut, l’inclusion fournie par Cache Factory se nomme formulaires/inc-cache_cache_vider.html. Un plugin qui souhaite « remplacer » cette inclusion doit créer sa propre inclusion nommée formulaires/inc-${plugin}_cache_vider.html. Si elle existe elle sera appelée en lieu et place de celle par défaut.

En général, pour produire un affichage différent que l’affichage par défaut, un plugin regroupe ses caches selon certains critères et/ou rajoute des données sur chaque cache. Il est souvent nécessaire de modifier le tableau de chargement du formulaire pour qu’il coïncide avec l’objectif d’affichage.

Pour ce faire, Cache Factory propose le service formulaire_charger(). Par exemple, le plugin Rainette modifie le chargement du formulaire avec le code contenu dans la fonction rainette_formulaire_charger().

Personnaliser les identifications et les descriptions d’un cache

Cache Factory possède des mécanismes par défaut pour composer le nom d’un fichier cache et le décomposer en composants unitaires. Ces mécanismes sont simples et non contextuels.

Pour constituer le nom d’un fichier cache le service natif cache_composer() lit les composants attendus (obligatoires et facultatifs) et les concatène en utilisant le séparateur configuré. Pour retrouver les composants à partir du nom du fichier cache, le service natif cache_decomposer() fait l’inverse en scindant le nom en composants. Les composants sont interprétés dans l’ordre donné par le paramètre de configuration nom sans aucune vérification sémantique.

Ces mécanismes sont en général suffisants pour la plupart des plugins. Néanmoins, pour un plugin utilisateur qui souhaiterait personnaliser la composition ou la décomposition du nom du fichier cache, Cache Factory lui offre la possibilité de fournir ses propres services en codant les fonctions ${plugin}_cache_composer() et ${plugin}_cache_decomposer() qui seront utilisées en lieu et place des services natifs.

Cache Factory propose également de compléter la description d’un cache renvoyée par l’API cache_repertorier(). La base de cette description est constituée du tableau fourni par le service cache_decomposer() auquel Cache Factory rajoute les index “nom_cache” et “extension_cache” au travers de son service natif cache_completer().

De même, ce mécanisme est souvent suffisant mais un plugin comme Taxonomie a besoin de rajouter le nom scientifique du taxon concerné par un cache pour l’affichage dans le formulaire. Il code donc son propre service taxonomie_cache_completer() pour rajouter cette information à la description retournée par cache_repertorier().

Résumé pour le développement d’un plugin utilisateur

Pour un plugin utilisateur, la mise en œuvre de Cache Factory peut se limiter à:

  • créer à la racine du plugin un fichier cache/${plugin}.php;
  • fournir un tableau de configuration générale en codant la fonction ${plugin}_cache_configurer();
  • et utiliser les API proposées décrites de façon exhaustive sur le site de documentation du code des plugins à l’adresse suivante: https://code.plugins.spip.net/cache/.

Un exemple classique d’utilisation des API est donné ci-dessous (plugin Spiper Ipsum) :

// Constituer l'identifiant relatif du cache
include_spip('inc/cache');
$cache = array(
	'sous_dossier' => $service,
	'date'         => $date,
	'langage'      => $code_langue
);
 
// Mise à jour du cache avec les nouvelles données si:
// - le fichier cache n'existe pas
// - la période de validité du cache est échue
// - le rechargement a été forcé
if ((!$fichier_cache = cache_est_valide('spiperipsum', $cache))
or (defined('_SPIPERIPSUM_FORCER_CHARGEMENT') ? _SPIPERIPSUM_FORCER_CHARGEMENT : false)) {
	// Utilisation de la fonction de chargement du service.
	$charger = "${service}_charger";
	$tableau = $charger($code_langue, $date);
 
	// Mise à jour du cache
	cache_ecrire('spiperipsum', $cache, $tableau);
} else {
	// Lecture des données du fichier cache valide
	$tableau = cache_lire('spiperipsum', $fichier_cache);
}

Certains plugins voudront également personnaliser l’affichage du formulaire de vidage en codant leur version du service formulaire_charger() et de l’inclusion formulaires/inc-${plugin}_cache_vider.html.

Ces deux personnalisations sont en général largement suffisantes pour 90% des plugins utilisateur.

Evolutions

Le plugin est dans sa phase de déploiement. Les plugins Taxonomie, Rainette, Spiper Ipsum et N-Core ont déjà adopté Cache Factory et ainsi simplifié le code de gestion de leurs fichiers cache. L’utilisation par d’autres plugins permettra surement d’envisager des améliorations à apporter à l’API actuelle.

N’hésitez donc à partager vos retours.

updated on 15 April 2019

Discussion

Aucune discussion

Comment on this article

Who are you?
  • [Log in]

To show your avatar with your message, register it first on gravatar.com (free et painless) and don’t forget to indicate your Email addresse here.

Enter your comment here

This form accepts SPIP shortcuts {{bold}} {italic} -*list [text->url] <quote> <code> and HTML code <q> <del> <ins>. To create paragraphs, just leave empty lines.

Add a document

Follow the comments: RSS 2.0 | Atom