Évolution de la gestion d’un plugin

, par Committo, Ergo Sum

Pour disposer d’un Plugin SPIP, il faut d’abord copier ses sources PHP et connexes dans un répertoire connu par SPIP pour contenir des Plugins, puis demander son activation, ce qui provoque éventuellement certaines initialisations.
Lorsque le site de développement du Plugin modifie ses sources , il faut en être prévenu, copier les nouveaux fichiers dans le même répertoire, puis provoquer la mise à jour éventuelle des initialiisations du début.
À l’inverse, si l’on veut se dispenser d’un Plugin actif, il faut le rendre inactif et éventuellement en supprimer les données.

Presque toutes ces opérations sont faites dans la version actuelle (2.1) de SPIP, mais avec une interface de programmation confuse dans sa terminologie, lacunaire sur certains points, redondante sur d’autres.
On propose ici une refonte de ces spécifications, fondée sur des statistiques d’utilisation effective des possibilités fournies, et cela dans le cadre Des DTD pour décrire et archiver les plugins implémentées par le Plugin Plugonet, première étape de la fusion des Plugins SVP et STEP pour gérer facilement la totalité des plugins disponibles pour SPIP.

L’existant

Actuellement, la disponibilité d’une nouvelle version de SPIP est annoncée automatiquement en bas de toutes les pages de l’espace privé, via l’interrogation régulière de l’adresse files.spip.org/spip/archives.xml par le mécanisme du génie.
Mais la version courante de SPIP n’offre pas la même fonctionnalité pour les Plugins utilisés par le site concerné. Seul le Plugin STEP fournit cette fonctionnalité, en interrogeant le fichier files.spip.org/spip-zone/archives.xml (et potentiellement d’autres si on les déclare), utilisant pour cela la fonction spip_version_compare, qui impose parfois des contraintes peu claires sur la syntaxe d’un numéro de version.

L’opération consistant à copier les sources d’un Plugin (que ce soit par FTP, SVN ou autres utilitaires) se nomme installation dans le noyau de SPIP comme dans le plugin STEP. Malheureusement ce vocable est aussi parfois utilisé pour désigner d’autres opérations.
Il est à remarquer que cette copie des sources n’a en soi aucune incidence sur le site si le plugin n’a pas été déclaré actif. S’il l’est déjà et que les sources sont mises à jour sans repasser par la page listant les Plugins (voir ci-dessous), il peut en résulter un dysfonctionnement du site sans aucun message explicatif. C’est d’autant plus surprenant que le cas beaucoup plus rare où les sources sont retirées sans désactiver le Plugin, bénéficie d’un message d’erreur fourni par une coûteuse analyse de présence des fichiers indiqués dans verifier_plugins.txt à chaque connexion à l’espace privé.

Lorsque l’on affiche la page Gestion des Plugins, SPIP ne se contente pas d’indiquer les Plugins actifs : il les active effectivement, et c’est même le seul endroit où cette opération a lieu une fois SPIP installé.
Le script admin_plugin n’obéit donc pas au modèle REST, contrairement aux autres scripts du répertoire exec.

Cette activation repose sur une interface de programmation complexe, qui commence par charger les fichiers indiqués par la balise install de plugin.xml. Ensuite, deux cas sont prévus.

  • S’il existe une fonction dont le nom est composé du préfixe du Plugin suivi de _install, cette fonction est appelée avec test comme premier argument. Si le résultat retourné est False, elle est appelée une deuxième fois avec install comme premier argument. Comme son nom ne l’indique pas, cette fonction réalise donc l’activation du Plugin, dans le sens où il devient opérationnel alors qu’il était auparavant seulement installé. Elle est appelée avec deux arguments supplémentaires, le préfixe du Plugin (clairement inutile puisque la fonction est nommée à partir de lui), et son éventuel numéro de version du schéma de ses tables dans la base de données SQL que ses sources sont censées gérer, information dont cette fonction n’a pas nécessairement besoin et qu’elle peut retrouver d’elle-même dans les donnés issu du fichier XML décrivant le Plugin.
  • Si cette fonction n’existe pas, mais qu’il existe un numéro de version du schéma des tables, une fonction par défaut (trompeusement nommée spip_plugin_install) est appelée selon le même scénario (un appel avec test suivi d’un appel avec install si résultat nul). Elle admet un second argument : le tableau décrivant le Plugin (ce qui est plus complet et efficace que les 2e et 3e argument de la fonction ci-dessus qui ne sont que des extraits arbitraires de ce tableau). Lorsque la fonction spip_plugin_install est appelée avec test comme argument, elle se contente de retourner en résultat la comparaison des versions active et installée, en utilisant spip_version_compare. Si la fonction est appelée avec install comme argument, elle applique, si elle existe, une fonction dont le nom est composé du préfixe du Plugin suivi de _upgrade, et sinon ne fait rien.

Le script admin_plugin offre une case à cocher pour désactiver le Plugin, et un lien accessible seulement au survol, qui dit le désinstaller.
En réalité l’opération (désactiver) consiste à réactiver tous les Plugins dont la case à cocher était cochée ; autrement dit cette opération n’est pas liée à des fonctions spécifiques au Plugin concerné, contrairement à l’activation.
Quant à l’opération (désinstaller), elle détruit les données SQL accumulées pendant l’utilisation du Plugin, destruction dont on demande la confirmation par une boîte de dialogue. En revanche, les sources du Plugin (qui avaient été installées) restent dans leur répertoire.
Cette opération de destruction est réalisée à l’aide d’une interface de programmation calquée sur la précédente et toujours aussi complexe.

  • S’il existe une fonction dont le nom est composé du préfixe du Plugin suivi de _install, cette fonction est appelée une première fois avec uninstall comme premier argument, puis une deuxième fois avec test. Si le résultat final est False, le plugin est retiré de la liste des Plugins actifs, autrement il y reste, la fonction étant censée avoir laissé les choses en l’état si elle n’a pu opérer la destruction, ce qui paraît peu crédible.
  • Si cette fonction n’existe pas, mais qu’il existe un numéro de version du schéma des tables, la fonction par défaut spip_plugin_install est de nouveau appelée avec le même scénario. Dans le cas uninstall, elle applique (si elle existe) une fonction dont le nom est composé du préfixe du Plugin suivi de _vider_tables, et sinon ne fait rien. En pratique, on constate que les fonctions ainsi nommées font plus que vider les tables, car elles les détruisent, et effacent la meta contenant le numéro de version de leur schéma. Plus rarement, elles effectuent quelques opérations supplémentaires.

Terminons cette revue de l’existant par une petite statistique. Dans les 922 Plugins de la zone (dossier _plugins_/), sont déclarées :

  • 24 fonctions "_uninstall"
  • 170 fonctions "_install"
  • 198 fonctions "_upgrade"
  • 203 fonctions "_vider_tables"

Pour le cas "_uninstall", la lecture des 24 cas ne justifie pas l’intérêt d’avoir nommé ainsi cette fonction plutôt que "_vider_tables", les Plugins concernés ayant un numéro de version de schéma qui provoquerait l’appel à cette autre fonction en l’absence de la première
(les seules exceptions sont des fonctions ne faisant rien).

Pour le cas "_install", il apparaît beaucoup de cas où la fonction n’a pas une signature conforme aux spécifications, et est alors sans effet. Beaucoup d’autres sont une copie de la fonction spip_plugin_install, sans rien apporter. D’autres enfin, trompés sans doute par le nom de l’opération, effectuent systématiquement une initialisation alors que parfois il ne faut qu’une mise à jour.

Pour le cas "_upgrade", les spécifications sont bien respectées, mais aucune de ces fonctions ne fait appel à la fonction standard maj_while de SPIP qui leur permettrait de réduire leur taille et de profiter d’un code bien éprouvé.

Pour le cas "_vider_tables" enfin, la ressemblance entre les fonctions est telle qu’il serait utile de disposer dans le noyau de SPIP d’une fonction générique, qu’on appellerait en lui fournissant le nom des tables concernées. Ces noms sont une information qui peut être utile par ailleurs, notamment pour des sauvegardes spécifiques et pour prévenir, avant de choisir ce Plugin, des noms des tables créées (qui peuvent être en conflit avec d’autres Plugins).

Propositions

Sur le plan de la terminologie, donc des libellés expliquant les actions possibles à partir des pages d’administration, il convient d’adopter les conventions suivantes :

  • l’installation est la copie des sources d’un Plugin dans un répertoire connu de SPIP ;
  • l’activation est l’ajout d’un Plugin dans la liste des plugins actifs ainsi que la déclaration de son schéma de données celui-ci étant essentiellement la liste de ses tables SQL mais pouvant aussi comporter d’autres données gérées dans d’autres tables ou dans des fichiers ; il est important de noter qu’on active une version du Plugin, et non le Plugin, ce qui veut dire qu’on provoque soit la création du schéma de données si le Plugin n’était pas déjà déclaré dans une version antérieure, soit sa mise à jour.
  • la suppression est l’effacement des données du Plugin et de leur schéma, ainsi que son retrait de la liste des Plugins actifs
  • la destruction est l’effacement des sources du Plugin, opération inverse de l’installation, et rarement utile.

Dans la mise en conformité XML du fichier de description d’un Plugin, il est proposé de rendre unique et de normaliser le nom du fichier à charger où se trouvent les fonctions réalisant les opérations ci-dessus, plutôt que de l’indiquer dans la balise installl, appelée à disparaître comme options et fonctions.
Dans la continuité des noms $prefix_options
et $prefix_fonctions, il paraît préférable de l’appeler $prefix_administrations, seul terme qui recouvre toutes les opérations ci-dessus.

Il semble également important de normaliser le numéro de version d’un Plugin, afin de déterminer facilement si une mise à jour existe. Comme pour les versions récentes de SPIP, il vaudrait mieux qu’il soit systématiquement composé de trois nombres entiers séparés par un point : x.y.z où un changement de x signale une incompatibilité, et un changement de y signale un ajout de fonctionnalité, tandis que z signale des corrections de bugs.
Rappelons que le nom d’un Plugin ne doit pas comporter de chiffres, afin d’éviter toute confusion.
Partant, il semble logique que le sous-répertoire d’un Plugin dans le répertoire _plugin_ d’un dépôt comporte autant de sous-répertoires que de valeurs x.y, chaque version étant a priori maintenue (donc z peut varier). Si l’on adopte ce point de vue, la branche tags du dépot SVN de la zone paraît superflue, et on pourrait produire automatiquement le fichier archivelist.txt nécessaire pour Le script Smart-Paquets actuel.

En ce qui concerne le numéro de version du schéma,
le rendre obligatoire
permettrait de supprimer le doublon constitué par les fonctions
$prefix_install et $prefix_upgrade,
qui repose sur le test de son existence.
Ces deux fonctions sont aisément remplaçables par la fonction standard maj_while de SPIP. Cette fonction reçoit en argument deux entiers n et m (le premier est la version active indiqué par la meta base_version du Plugin, le deuxième la version des sources indiquée par le fichier de description XML) et un tableau. Elle
appelle les fonctions PHP indiquése par les i-èmes cases du tableau comprises entre n et m quand elles sont définies, appels faisant passer à la version i du schéma de données.
De plus, si deux arguments supplémentaires sont fournis à maj_while, à savoir le nom d’une meta et sa table (si le fichier de description XML indique une telle table, le nom est base_version, sinon le nom est $prefix_base_version et
la table est celle standard de SPIP), cette meta aura pour valeur finale m (et à chaque itération les valeurs intermédiaires, ce qui permet une reprise sur interruption).

Afin que les développeurs de Plugins puissent profiter de cette fonction, il convient d’imposer que le numéro de version du schéma soit un nombre entier.
Pour retrouver facilement la raison d’un changement, il est conseillé de prendre, comme SPIP,
le numéro de dépot qui introduit ce changement.
On peut aussi prendre la date de ce changement, sous forme AAAAMMJJHHMM.
Le code installant le schéma pour la première fois peut être placé à l’index 0 mais ce n’est pas indispensable.

Enfin, pour que SPIP puisse automatiquement appeler la fonction maj_while pour un Plugin, il faut convenir d’un nom pour le tableau global contenant le code de mise à jour, on prendra $prefix_maj.
Chaque entrée de ce tableau est un tableau de sous-tableaux, chacun ayant comme premier élément une fonction, les suivants étant ses arguments. Cette fonction est souvent sql_alter, mais n’importe quelle fonction est autorisée, en particulier pour effectuer d’autres opérations que celles concernant la base de données.
Signalons enfin que cette organisation a l’avantage que si une mise à jour se révèle erronée, il suffit d’utiliser unset sur l’indice concerné pour neutraliser son code, tout en le conservant pour comprendre l’état des mises à jour l’ayant déjà exécuté et agir en conséquence.

Comme on l’a signalé, les fonctions nommées vider_tables se réduisent le plus souvent à supprimer les tables du schéma de données du Plugin. Il vaudrait mieux que la liste de ces tables soit contenue dans des globales, afin que SPIP puisse automatiquement effectuer cette suppression, et disposer de ces informations pour d’autres opérations éventuelles (sauvegardes, description etc).
Comme il y a deux sortes de tables (avec ou sans auto-incrément), chaque Plugin déclarerait deux listes, nommées $prefix_principales et $prefix_auxiliaires. SPIP supprimera également la meta indiquant le numéro de version du schéma, dans le cas où elle figure dans la table des métas de SPIP. Pour les rares cas où d’autres opérations sont à effectuer (destructions de champs sur d’autres tables par exemple), on pourrait convenir d’appeler une fonction nommée $prefix_supprime (moins équivoque que uninstall) qui serait appelée avant la destruction des tables et de la meta par SPIP (ces informations pouvant être nécessaires à cette fonction).